[海军国际项目办公室]七负我
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[海军国际项目办公室]七负我相关的知识,希望对你有一定的参考价值。
七负我
题目概述
题解
这出题人好臭呀,为什么要去JKLover这样的名字。
首先,我们可以得到结论,我们的最优选择,肯定是在一个完全图的所有点上均等时间地打工。
显然,一个完全图可以使我们贡献尽可能多的边。
假设我们对于点对
(
u
,
v
)
(u,v)
(u,v),两者之间没有边,
u
u
u与
v
v
v的权值分别为
t
u
t_{u}
tu与
t
v
t_{v}
tv,
u
u
u的邻接点的权值和
s
u
s_u
su,
v
v
v的邻接点的权值和为
s
v
s_v
sv。
显然,这两个点的邻接边的贡献和为
(
s
u
t
u
+
s
v
t
v
)
(s_ut_u+s_vt_v)
(sutu+svtv)。显然,
(
u
,
v
)
(u,v)
(u,v)之间没有边,
t
u
t_u
tu不被包含在
s
v
s_v
sv内,
t
v
t_v
tv不被包含在
s
u
s_u
su内。
不妨设
s
u
⩾
s
v
s_u\\geqslant s_v
su⩾sv,如果我们保持
t
u
+
t
v
t_u+t_v
tu+tv的和不变,让
t
u
=
t
u
+
t
v
t_u=t_u+t_v
tu=tu+tv,
t
v
=
0
t_v=0
tv=0,那么我们的贡献时不会变得更劣的。
所以我们一定有一种最优的情况所有有权值点构成的导出子图是一个完全图。
而既然已经是一个完全图了,那么肯定所有点都是等价的,所以每个点分到的点权都是一样的。
所以上面的结论就很好证明了。
显然,我们只需要求出整张图最大团(即完全图)就可以了,毕竟比该团大小小的团都可以从该团的子集中选出。或许题目名其实应该叫 团不过 的
求团我们显然有一个
O
(
2
n
n
)
O\\left(2^nn\\right)
O(2nn)的状压做法,但这面对
n
⩽
40
n\\leqslant 40
n⩽40的数据范围确实无能为也已的。
但我们应该很容易想到折半的形式,用 meet in middle 的想法,对于前一半和后一半分别状压,压后一半时记录下后一半可能与前面的哪些点构成团。
前面状压时顺便记录下来该点集内的最大团,与后一半的团合并起来求出最大团。
时间复杂度 O ( 2 n 2 n ) O\\left(2^{\\frac{n}{2}}n\\right) O(22nn)。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN (1<<24)+5
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,m,x,dp[MAXN],f[MAXN],g[MAXN],bit[MAXN],ip[45],a[45],maxx;
bool mp[45][45];double ans;
signed main(){
freopen("nanami.in","r",stdin);
freopen("nanami.out","w",stdout);
read(n);read(m);read(x);int hf=(n+1)/2;
for(int i=1;i<=m;i++){
int u,v;read(u);read(v);
mp[u][v]=mp[v][u]=1;
}
for(int i=1;i<=n;i++)for(int j=1;j<=hf;j++)a[i]|=(mp[i][j]<<j-1);
for(int i=1;i<(1<<max(hf,n-hf));i++)bit[i]=bit[i>>1]+(i&1);
for(int i=1;i<(1<<hf);i++){
int t=lowbit(i),id=0;dp[i]=dp[i^t];while(t)t>>=1,id++;
for(int j=1;j<=hf;j++)if((i&(1<<j-1))&&mp[id][j])dp[i]++;
for(int j=1;j<=hf;j++)if(i&(1<<j-1))f[i]=max(f[i^(1<<j-1)],f[i]);
if(dp[i]==bit[i]*(bit[i]-1)/2)f[i]=bit[i];
}
for(int i=1;i<(1<<n-hf);i++)dp[i]=0;maxx=f[(1<<hf)-1];g[0]=(1<<hf)-1;
for(int i=1;i<(1<<n-hf);i++){
int t=lowbit(i),id=0;dp[i]=dp[i^t];g[i]=g[i^t];while(t)t>>=1,id++;
for(int j=1;j<=n-hf;j++)if((i&(1<<j-1))&&mp[id+hf][j+hf])dp[i]++;
g[i]&=a[id+hf];if(dp[i]==bit[i]*(bit[i]-1)/2)maxx=max(maxx,f[g[i]]+bit[i]);
}
for(int i=2;i<=maxx;i++){
double t=1.0*x/i;int num=i*(i-1)/2;
ans=max(1.0*num*t*t,ans);