題目
實際上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)); }