CF888G Xor-MST

題目
實際上Kruskal和Boruvka都是可行的,這裏講一個比較有意思的作法。
從高位往低位考慮,對於當前枚舉到的位,咱們把全部當前位爲\(1\)的點看作一個集合,全部當前位爲\(0\)的點看作一個集合。
那麼顯然這兩個集合之間要連至少一條邊,而且也只能連一條邊(證實能夠考慮模擬Kruskal的過程)。
那麼咱們就能夠利用trie樹\(O(n)\)地求出兩個集合間的最短邊。
而後剩下的兩個集合分治處理。
每個點在每一位最多被加入trie樹一次,因此總複雜度爲\(O(n\log^2a)\)c++

#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));
}
相關文章
相關標籤/搜索