码迷,mamicode.com
首页 > 其他好文 > 详细

UOJ176 新年的繁荣

时间:2019-12-06 09:56:08      阅读:91      评论:0      收藏:0      [点我收藏+]

标签:bit   子集   生成树   return   std   can   存在   传递   find   

题意

有一张\(n\)个点的完全图,点权为\(a[i]\)\(w_{i,j}=a_i \mathbin{\mathrm{and}} a_j\)。问这个图的最大生成树。
\(n \leq 10^5,a[i]<2^{18}\)

思路

本来想写个\(Boruvka\),然后发现可以用熟知的\(Kruskal\)过掉。
因为边权很小,所以可以枚举边权,考虑这条边有没有贡献。
可以考虑如果两个点点权相同,那么它们连起来,一定是最优的。这样我们就可以直接加上相同点的贡献,然后留下互不相同的点,以他们的点权为编号。
对于\(i\)的边权,我们可以枚举所有\(x \& y=i\),如果存在\(x,y\)\(father[x] \neq father[y]\)那么就贪心连起来。但是枚举\(x,y\)实在是做不到,因此我们可以将\(x\)传递到\(x\)的子集中,这样子就只需枚举\(i|2^j\)了。

#include <bits/stdc++.h>
const int N=100005;
int a[1<<20],f[1<<20],n,m,x;
long long ans;
int find(int x){
    if (f[x]==x) return x;
    f[x]=find(f[x]);
    return f[x];
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
        scanf("%d",&x);
        if (a[x]) ans+=x;
        a[x]=x;
    }
    for (int i=1;i<1<<m;i++) f[i]=i;
    for (int i=(1<<m)-1;i>=1;i--){
        for (int j=0;j<m && !a[i];j++)
            a[i]=a[i|(1<<j)];
        int fx=find(a[i]);
        for (int j=0;j<m;j++){
            if (!a[i|(1<<j)]) continue;
            int fy=find(a[i|(1<<j)]);
            if (fx==fy) continue;
            ans=ans+i;
            fx=f[fx]=fy;
        } 
    }
    printf("%lld\n",ans);
} 

后记

被吐槽好久没更了,所以来写一篇不是那么清楚的

UOJ176 新年的繁荣

标签:bit   子集   生成树   return   std   can   存在   传递   find   

原文地址:https://www.cnblogs.com/flyfeather6/p/11993206.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!