【学习笔记】CF1305 Kuroni and Antihype
想了一下,觉得还是发单篇的题解比较合理
怎么感觉这题之前做过
先抛开建边方式不管 这一步其实挺重要的,但是可能大多数人独立做这道题的时候都在想用位运算的性质,而没有想到分开考虑吧?,考虑新建
0
0
0号节点,问题转化为如果
a
i
and
a
j
=
0
a_i\ \text{and}\ a_j=0
ai and aj=0,那么存在
i
→
j
i\to j
i→j的长度为
a
j
a_j
aj的边,以及
j
→
i
j\to i
j→i的长度为
a
i
a_i
ai的边,求以
0
0
0为根节点的最大树形图。
观察发现边权和等于将每条边看成 a i + a j a_i+a_j ai+aj求和后再减去 ∑ a i \sum a_i ∑ai,因此无向图的生成树也对应一个树形图。
因此可以直接跑
kruskal
\text{kruskal}
kruskal算法。从大到小枚举边权,然后枚举子集,注意一下细节应该可以通过。复杂度
O
(
3
18
)
O(3^{18})
O(318)。时限开3s还是比较稳的
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int cnt[1<<18],vs[1<<18];
int n,m,fa[1<<18],a[1<<18];
ll res;
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void unionset(int x,int y){
int u=find(x),v=find(y);
if(u!=v){
m-=cnt[u]+cnt[v]-1;
res+=(ll)(cnt[u]+cnt[v]-1)*(x|y);
fa[u]=v,cnt[v]=1;
}
}
int main(){
cin>>n;
cnt[0]++;
for(int i=0;i<1<<18;i++)fa[i]=i,vs[i]=0;
for(int i=1;i<=n;i++){
cin>>a[i],cnt[a[i]]++;
}
for(int i=(1<<18)-1;i>=0;i--){
for(int j=i;j;j=(j-1)&i){
if(cnt[j]&&cnt[i-j]){
unionset(j,i-j);
}
}
}
for(int i=1;i<=n;i++)res-=a[i];
cout<<res;
}

浙公网安备 33010602011771号