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 imes 10^5)

(0 le a_i le 2 imes 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
",an);
	return 0;
}
原文地址:https://www.cnblogs.com/ljzalc1022/p/12976159.html