CF1305G - Kuroni and Antihype

CF1305G - Kuroni and Antihype

题目大意

\(n\) 个人,每个人有一个权值 \(a_i\)

每个人可以自己选择放入集合,不获得分数

或者一个已经在集合中的人 \(i\) 可以把一个 \(a_i \ \text{and}\ a_j=0\)\(j\) 放入集合,并且获得 \(a_i\) 的分数

求最大得分总和


模型分析

按照原题的模型分析,视 \(a_i\rightarrow a_j\) 为一条权值为 \(a_i\) 边,则实际上求的是 最大外向森林

考虑加入 \(a_0=0\) ,且 \(a_0\) 初始在集合中,一个人自己放入集合视作被 \(0\) 放入集合

那么问题简化为求以0为根的 最大外向树

进一步观察会发现:

在外向树上每个点贡献次数就是 \(a_i\cdot (deg_i-1)\)

那么最大化 \(a_i\cdot (deg_i-1)\) 等价于最大化 \(\sum a_i\cdot deg_i\)

那么我们将一条边的权值 \(a_i\rightarrow a_j\) 改为 \(a_i+a_j\) ,此时边双向权值相同

问题就变成了求最大生成树


计算生成树

只有暴力解法

倒着枚举 \(a_i+a_j=S\) ,枚举 \(S\) 的子集 \(T\) 就能确定两个点

并查集处理加边即可,复杂度为 \(O(\cfrac{3^{18}}{2}\cdot \alpha(n))\)

\(\text{CodeForces}\) 真的有点快!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const int N=1<<18,INF=1e9+10;

int n,a[N],F[N];
ll ans;
int Find(int x){ return F[x]==x?x:F[x]=Find(F[x]); }

int main(){
n=rd();
rep(i,1,n) {
int x=rd();
a[x]++,ans-=x;
}
a[0]++;
rep(i,0,N-1) F[i]=i;
drep(i,N-1,1) {
for(int S=i,T;(S^i)<=S;S=(S-1)&i) if(a[S] && a[T=S^i] && Find(S)!=Find(T)) {
ans+=1ll*(a[S]+a[T]-1)*i;
a[S]=a[T]=1,F[Find(S)]=Find(T);
}
}
printf("%lld\n",ans);
}