CodeForces 1305G Kuroni and Antihype

CodeForces 1305G Kuroni and Antihype

https://codeforces.com/contest/1305/problem/G

有一个组织,它的奖励机制如下.

  • 如果一个人加入了组织,他的奖励为 \(0\)
  • 如果一个人邀请了他的朋友加入了组织,他的奖励为他自己的年龄.

\(n\) 个人想要通过合作加入这个组织使得他们的奖励之和最大,其中第 \(i\) 个人的年龄为 \(a_i\) .特别的, \(i,j\) 两人为朋友当且仅当 \(a_i \ AND\ a_j=0\) .

问最大奖励之和.

\(1 \le n \le 2 \times 10^5\)

\(0 \le a_i \le 2 \times 10^5\)

Tutorial

首先假设有一个 \(a_0=0\) 的人已经在组织内,那么就仅有邀请加入这一种方式.

假设所有朋友关系的人之间有一条边,对于一条边 \((u,v)\) ,如果 \(u\) 邀请了 \(v\)\(v\) 邀请了 \(u\) ,则令这条边为红色,发现所有红色的边形成了一个树的结构.

\(a_0=0\) 为这棵树的根,则每个点 \(i\) 的贡献为 \(a_i\) 乘上儿子数.考虑令每条边 \((u,v)\) 的边权为 \(a_u+a_v\) ,发现答案就是边权和减去所有 \(a_i\) 的和.

所以现在将其转化为最大生成树的问题,可以采用Kruskal的思路,枚举 \(s=2^{18}-1,2^{18}-2,\cdots,0\) .和 \(t \in s\) ,加入 \(t\)\(s \ XOR \ t\) 的连边,它们的边权为 \(s\) .

复杂度 \(O(3^{18})\)

Code

https://codeforces.com/contest/1305/submission/72366838

#include <cstdio>
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
typedef long long ll;
const int maxn=1<<18;
int n;
int cnt[maxn];
bool vis[maxn];
namespace us
{
	int fa[maxn];
	void init(int n)
	{
		for(int i=0;i<=n;++i) fa[i]=i;
	}
	int find(int a) {return a==fa[a]?a:fa[a]=find(fa[a]);} 
	int merge(int a,int b)
	{
		a=find(a),b=find(b); if(a==b) return 0;
		int re=-1;
		re+=vis[a]?1:cnt[a]; vis[a]=1;
		re+=vis[b]?1:cnt[b]; vis[b]=1;
		fa[a]=b;
		return re;
	}
}
int main()
{
	scanf("%d",&n);
	ll an=0;
	for(int i=1;i<=n;++i)
	{
		int x; scanf("%d",&x);
		++cnt[x];
		an-=x;
	}
	++cnt[0]; 
	us::init((1<<18)-1);
	for(int s=(1<<18)-1;~s;--s)
	{
		for(int t=s;;t=(t-1)&s) 
		{
			if(cnt[t]&&cnt[s^t])
			{
				an+=(ll)s*us::merge(t,s^t);
			} 
			if(t==0) break;
		}
	}
	printf("%I64d\n",an);
	return 0;
}
posted @ 2020-05-28 17:35  LJZ_C  阅读(188)  评论(0编辑  收藏  举报