题解 七负我

传送门

又是神仙题……

首先难点在于一个结论:一定至少存在一个最优解是一个团
题解证明不好想到
\(u,v\) 之间没有边,记 \(s_u,s_v\) 分别表示与 \(u,v\) 相连的点的 \(t\) 之和。
\(u,v\) 的总贡献应当是 \(s_u ×t_u + s_v ×t_v\)
若保证 \(t_u+t_v\) 不变,将其中一个调整到0
\(s_u=s_v\) 时,结果不变
否则将 \(s\) 较小的那个调整到0,结果变大
于是得证
所以问题变成了找最大团

  • 关于找最大团:
    这个挺适合随机化找的
    有一个meet in middle的找法:
    将原图分为前p个和后n-p个
    处理 \(f(s)\) 表示 \(s\) 是否是团
    处理 \(g(s)\) 表示 \(s\) 的导出子图中的最大团大小
    于是从后n-p中枚举团 \(T\),令 \(out\) 为这个团中元素的出边交
    于是 \(ans=max(ans, |T|+g(out))\)
    注意 \(n\) 较大的话读入要开long long
    还有一个Bron–Kerbosch算法,先咕了
    其实如果图比较大的话可以直接爆搜剪枝,有这个题
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 50
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m, k1, k2;
double x;
int f[1<<20], g[1<<20], ans;
ll mp[N];

signed main()
{
	freopen("nanami.in", "r", stdin);
	freopen("nanami.out", "w", stdout);

	n=read(); m=read(); x=read();
	k1=n/2; k2=n-k1;
	// cout<<"k: "<<k1<<' '<<k2<<endl;
	int lim=1<<k1, cls=(1<<k1)-1;
	for (int i=0; i<n; ++i) mp[i]|=1ll<<i;
	for (int i=1,u,v; i<=m; ++i) {
		u=read()-1; v=read()-1;
		mp[u]|=1ll<<v; mp[v]|=1ll<<u;
	}
	f[0]=1;
	for (int s=0; s<lim; ++s) if (f[s]) {
		int out=lim-1;
		for (int i=0; i<k1; ++i) if (s&(1<<i)) out&=mp[i]&cls;
		for (int i=0; i<k1; ++i) if (!(s&(1<<i)) && out&(1<<i) && (s&(mp[i]&cls))==s) f[s|(1<<i)]=1;
	}
	for (int s=0; s<lim; ++s) {
		if (f[s]) g[s]=__builtin_popcount(s); //, cout<<"pos1"<<endl;
		else for (int i=0; i<k1; ++i) if (s&(1<<i)) g[s]=max(g[s], g[s^(1<<i)]);
		// cout<<"g: "<<g[s]<<endl;
	}
	lim=1<<k2;
	memset(f, 0, sizeof(f));
	f[0]=1;
	for (int s=0; s<lim; ++s) if (f[s]) {
		int out=lim-1;
		for (int i=0; i<k2; ++i) if (s&(1<<i)) out&=mp[i+k1]>>k1;
		for (int i=0; i<k2; ++i) if (!(s&(1<<i)) && out&(1<<i) && (s&(mp[i+k1]>>k1))==s) f[s|(1<<i)]=1;
		out=(1<<k1)-1;
		for (int i=0; i<k2; ++i) if (s&(1<<i)) out&=mp[i+k1]&cls;
		ans=max(ans, __builtin_popcount(s)+g[out]);
	}
	// cout<<"ans: "<<ans<<endl;
	printf("%.6lf\n", (x/ans)*(x/ans)*(ans*(ans-1)/2));

	return 0;
}
posted @ 2021-10-30 07:22  Administrator-09  阅读(0)  评论(0编辑  收藏  举报