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;
}