Xor-MST
阿新 • • 發佈:2020-09-10
題意
給出一個 \(n\) 個點的無向完全圖,每個點的點權為:\(a_i\),每條邊的權值為該邊兩個端點的點權的異或值。求出這個圖最小生成樹的權值。
\(1\leq n \leq 200000,0\leq a_i < 2^{30}\)
題目連結:https://codeforces.com/problemset/problem/888/G
分析
最小異或生成樹,把點權儲存在字典樹上進行匹配。
程式碼
#include <bits/stdc++.h> #define pb push_back using namespace std; typedef long long ll; const int N=2e5+5; const int maxn=3e6+5; int a[N],trie[maxn][2],cnt; int id[maxn]; vector<int>value[N]; ll ans; void add(int x,int k) { int rt=1; for(int i=29;i>=0;i--) { int t=((x>>i)&1); if(trie[rt][t]==0) trie[rt][t]=++cnt; rt=trie[rt][t]; } id[rt]=k; value[k].pb(x); } int matching(int x,int rt,int d) { int res=(1<<d); for(int i=d-1;i>=0;i--) { int t=((x>>i)&1); if(trie[rt][t]>0) rt=trie[rt][t]; else { rt=trie[rt][1-t]; res|=(1<<i); } } return res; } void solve(int rt,int d)//注意d的取值,字典樹以邊表示二進位制位的值 { if(trie[rt][0]>0) solve(trie[rt][0],d-1); if(trie[rt][1]>0) solve(trie[rt][1],d-1); if(trie[rt][0]>0&&trie[rt][1]>0)//分叉點 { int x=id[trie[rt][0]],y=id[trie[rt][1]]; int min_xor=(1<<30); if(value[x].size()<value[y].size())//選取小的子樹 { for(int i=0;i<value[x].size();i++) { int tmp=value[x][i]; int xr=matching(tmp,trie[rt][1],d-1); min_xor=min(xr,min_xor); value[y].pb(tmp); } id[rt]=y; } else { for(int i=0;i<value[y].size();i++) { int tmp=value[y][i]; int xr=matching(tmp,trie[rt][0],d-1); min_xor=min(xr,min_xor); value[x].pb(tmp); } id[rt]=x; } ans+=min_xor; } else {//上傳到父親節點 if(trie[rt][0]>0||trie[rt][1]>0) id[rt]=id[trie[rt][0]+trie[rt][1]]; } } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n); cnt=1; add(a[1],1); for(int i=2;i<=n;i++) { if(a[i]!=a[i-1]) add(a[i],i); } ans=0; solve(1,30); printf("%lld\n",ans); return 0; }