CF888G Xor-MST

题目
实际上Kruskal和Boruvka都是可行的,这里讲一个比较有意思的做法。
从高位往低位考虑,对于当前枚举到的位,我们把所有当前位为\(1\)的点看做一个集合,所有当前位为\(0\)的点看做一个集合。
那么显然这两个集合之间要连至少一条边,并且也只能连一条边(证明可以考虑模拟Kruskal的过程)。
那么我们就可以利用trie树\(O(n)\)地求出两个集合间的最短边。
然后剩下的两个集合分治处理。
每一个点在每一位最多被加入trie树一次,所以总复杂度为\(O(n\log^2a)\)

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
namespace IO
{
    char ibuf[(1<<21)+1],*iS,*iT;
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
}
using namespace IO;
const int N=200007;
int min(int a,int b){return a<b? a:b;}
namespace trie
{
    int ch[N*30][2],cnt;
    void ins(int &p,int x,int d)
    {
    if(!p) p=++cnt,ch[p][0]=ch[p][1]=0;
    if(!~d) return ;
    ins(ch[p][x>>d&1],x,d-1);
    }
    int ask(int p,int x,int d)
    {
    if(!~d) return 0;int c=x>>d&1;
    return ch[p][c]? ask(ch[p][c],x,d-1):ask(ch[p][!c],x,d-1)|1<<d;
    }
}using namespace trie;
ll solve(vector<int>vec,int d)
{
    if(!~d||!vec.size()) return 0;
    vector<int>a[2];int ans=0,root;
    for(int x:vec) a[x>>d&1].pb(x);
    if(a[0].size()&&a[1].size())
    {
    ans=1<<d+1,root=cnt=0;
    for(int x:a[0]) ins(root,x,29);
    for(int x:a[1]) ans=min(ans,ask(root,x,29));
    }
    return ans+solve(a[0],d-1)+solve(a[1],d-1);
}
int main()
{
    int n=read();vector<int>a;
    for(int i=1;i<=n;++i) a.pb(read());
    printf("%lld",solve(a,29));
}

猜你喜欢

转载自www.cnblogs.com/cjoierShiina-Mashiro/p/11919500.html