CF888G Xor-MST

cf

luogu

这题\(prim\)\(kruskal\)似乎都不可做,考虑\(Boruvka\)算法,维护一堆连通块,对于每个连通块每次找出其他连通块和它的最小权值的边,然后只用这些边合并连通块,首先这样子做是对的,因为参考\(prim\),连通块应该用最小权的边和其他连通块合并,并且每次合并连通块数量至少少一半,所以只要做\(logn\)次.至于找其他连通块到它的最小权值的边,可以考虑\(trie\),每次把自己联连通块内的点在\(trie\)上的点删掉,然后枚举连通块内每个点查询与其他点的异或最小值

#include
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=2e5+10;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int n,a[N],ff[N],stk[N][3],tp;
int findf(int x){return ff[x]==x?x:ff[x]=findf(ff[x]);}
int q[N],hd,tl,op[N*30],tot;
int s[N*30],ch[N*30][2],id[N*30],rt,tt;
vector e[N];
vector::iterator it;
LL ans;

int main()
{
    n=rd();
    rt=++tt;
    int rs=n-1;
    for(int i=1;i<=n;++i) a[i]=rd();
    sort(a+1,a+n+1);
    for(int i=1;i<=n;++i)
    {
        ff[i]=i;
        int o=rt;
        ++s[o];
        for(int j=29;~j;--j)
        {
            int xx=a[i]>>j&1;
            if(!ch[o][xx]) ch[o][xx]=++tt;
            o=ch[o][xx];
            ++s[o];
        }
        if(id[o]) ff[id[o]]=i,e[i].push_back(id[o]),--rs; 
        id[o]=i;
    }
    while(rs)
    {
        for(int i=1;i<=n;++i)
            if(findf(i)==i)
            {
                hd=1,q[tl=1]=i;
                while(hd<=tl)
                {
                    int x=q[hd++],o=1;
                    op[++tot]=o;
                    --s[o];
                    for(int j=29;~j;--j)
                    {
                        int xx=a[x]>>j&1;
                        o=ch[o][xx];
                        --s[o];
                        op[++tot]=o;
                    }
                    for(it=e[x].begin();it!=e[x].end();++it)
                        q[++tl]=*it;
                }
                int zz=0,mi=1<<30;
                hd=1,q[tl=1]=i;
                while(hd<=tl)
                {
                    int x=q[hd++],o=1;
                    for(int j=29;~j;--j)
                    {
                        int xx=a[x]>>j&1;
                        if(s[ch[o][xx]]) o=ch[o][xx];
                        else o=ch[o][xx^1];
                    }
                    if(mi>(a[x]^a[id[o]])) mi=a[x]^a[id[o]],zz=id[o];
                    for(it=e[x].begin();it!=e[x].end();++it)
                        q[++tl]=*it;
                }
                stk[++tp][0]=i,stk[tp][1]=findf(zz),stk[tp][2]=mi;
                while(tot) ++s[op[tot]],--tot;
            }
        while(tp)
        {
            int x=findf(stk[tp][0]),y=findf(stk[tp][1]),z=stk[tp][2];
            if(x!=y)
            {
                ans+=z;
                ff[y]=x;
                e[x].push_back(y);
                --rs;
            }
            --tp;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(CF888G Xor-MST)