CF888G Xor-MST 解題報告
阿新 • • 發佈:2019-01-04
CF888G Xor-MST
題意翻譯
給定一個\(n\)個節點的完全圖,每個節點有個編號\(a_i\),節點\(i\)和節點\(j\)之間邊的權值為\(a_i\ xor\ a_j\),求該圖的最小生成樹的權值和。
說明
\(1\le n \le 200000,0\le a_i< 2^{30}\)
有一種B開頭的MST演算法
大致流程是這樣的
當前每個集合伸出去一條最短的邊,然後把聯通塊縮成一個新的集合,因為每次縮集合個數減半,所以複雜度對。
事實上基本不會讓我們真去寫它,有時候需要用這種方法或者思想處理處MST。
比如這個題就可以這麼做,每次合併實際上對trie樹做啟發式合併,然後在裡面查一下最小值。
然後有一種神奇的思路,這裡sto attack巨佬
發現每次合併的集合都是最高位的1不同的兩個集合進行合併,於是可以從上往下做,從最高位把集合分開,然後查詢兩個集合的最小連邊。
如果我們把所有元素按從小到大排序加入trie,那麼一個集合內的元素就在一個連續的區間裡啦
這樣我們就可以非常方便的直接遍歷一遍字典樹統計出答案了
Code:
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long using std::min; using std::max; const int N=2e5+10; const int inf=0x3f3f3f3f; #define ls ch[now][0] #define rs ch[now][1] int L[N*40],R[N*40],ch[N*40][2],tot; int n,a[N],root; void Insert(int &now,int x,int dep) { if(!now) now=++tot; L[now]=min(L[now],x),R[now]=max(R[now],x); if(dep<0) return; int bit=a[x]>>dep&1; Insert(ch[now][bit],x,dep-1); } int query(int now,int val,int dep) { if(dep<0) return 0; int bit=val>>dep&1; if(ch[now][bit]) return query(ch[now][bit],val,dep-1); else return query(ch[now][bit^1],val,dep-1)+(1<<dep); } ll dfs(int now,int dep) { if(dep<0) return 0; if(R[ls]&&R[rs]) { int mi=inf; for(int i=L[ls];i<=R[ls];i++) mi=min(mi,query(rs,a[i],dep-1)); return dfs(ls,dep-1)+dfs(rs,dep-1)+mi+(1<<dep); } if(R[ls]) return dfs(ls,dep-1); if(R[rs]) return dfs(rs,dep-1); return 0; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",a+i); std::sort(a+1,a+1+n); memset(L,0x3f,sizeof(L)); for(int i=1;i<=n;i++) Insert(root,i,30); printf("%lld\n",dfs(root,30)); return 0; }
2019.1.4