CF888G Xor-MST Boruvka、分治、Trie樹合並
阿新 • • 發佈:2019-02-28
方法 std tps iomanip 大小 http force push 得到
。
樹建好傳給上面一層,那麽每一層可以獲得這一位為\(1\)的所有數的\(Trie\)和這一位為\(0\)的所有數的\(Trie\)。將點數較少的點集中所有點的權值放在點數較多的點集對應的\(Trie\)上跑最小值,就可以得到當前層連邊的權值大小。計算完貢獻後將兩個\(Trie\)用類似線段樹合並的方式合並,可以有效避免\(MLE\)。
傳送門
第一次接觸到Boruvka求最小生成樹
它的原版本是:初始每一個點構成一個連通塊,每一次找到每一個連通塊到其他的連通塊權值最短的邊,然後合並這兩個連通塊。因為每一次連通塊個數至少減半,所以復雜度是\(O((n+m)logn)\)的
雖然它的原版本用途不多,但是思想可以涵蓋很多其他題目,比如這道題
可以想到一個做法:將所有權值插入一個\(Trie\)裏,在每一個葉子節點維護到達這個節點的數的編號。像上面那樣維護若幹連通塊,每一次計算權值最小的邊時,將當前連通塊中所有權值從Trie中刪去,然後對於連通塊中的每個權值在\(Trie\)上找到異或和最小的數字和編號,最後連邊、恢復原來的\(Trie\)
復雜度\(O(nlog^2n)\),但常數太大,哪怕在\(CF\)的神機下大數據也會直接淪陷QAQ
考慮:設能夠產生貢獻的二進制最高位為\(k\),即對於所有數來說,存在第\(k\)位為\(0\)的數,也存在第\(k\)位為\(1\)的數,且對於\(>k\)的數均不滿足這一條件。那麽最優的連邊方法顯然是:這一位為\(1\)的數之間連成一個生成樹,這一位為\(0\)的數之間連成一個生成樹,然後在這兩個點集之間連一條邊。可以發現這個問題變成了兩個子問題,且對於這兩個子問題的\(k\)一定會小於當前問題的\(k\),所以可以直接遞歸下去。
考慮如何計算當前層連的邊的貢獻。不妨讓每一層遞歸結束時把當前層所有權值對應的\(Trie\)
總復雜度仍然是\(O(nlog^2n)\)但跑得快了不少。
#include<iostream> #include<cstdio> #include<cctype> #include<algorithm> #include<cstring> #include<iomanip> #include<vector> #include<set> //This code is written by Itst using namespace std; inline int read(){ int a = 0; char c = getchar(); while(!isdigit(c)) c = getchar(); while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return a; } const int MAXN = 2e5 + 3; struct node{ node *ch[2]; node(){ch[0] = ch[1] = NULL;} }; struct Trie{ node *rt = new node; void ins(int x){ node *cur = rt; for(int i = 29 ; i >= 0 ; --i){ if(cur->ch[(bool)(x & (1 << i))] == NULL) cur->ch[(bool)(x & (1 << i))] = new node; cur = cur->ch[(bool)(x & (1 << i))]; } } int query(int x){ int ans = 0; node *cur = rt; for(int i = 29 ; i >= 0 ; --i){ bool f = x & (1 << i); if(cur->ch[f] != NULL) cur = cur->ch[f]; else{ cur = cur->ch[!f]; ans += 1 << i; } } return ans; } }; int N; long long sum; vector < int > val; node* merge(node *A , node *B){ if(A == NULL) return B; if(B == NULL) return A; A->ch[0] = merge(A->ch[0] , B->ch[0]); A->ch[1] = merge(A->ch[1] , B->ch[1]); return A; } Trie merge(Trie A , Trie B){ A.rt = merge(A.rt , B.rt); return A; } Trie solve(vector < int > val , int now){ if(val.empty()) return Trie(); if(now < 0){ Trie t; t.ins(val[0]); return t; } vector < int > lft , rht; for(auto t : val) t & (1 << now) ? rht.push_back(t) : lft.push_back(t); Trie L = solve(lft , now - 1) , R = solve(rht , now - 1); if(lft.size() < rht.size()){ swap(lft , rht); swap(L , R); } int minN = 2e9; for(auto t : rht) minN = min(minN , L.query(t)); if(!rht.empty()) sum += minN; return merge(L , R); } int main(){ #ifndef ONLINE_JUDGE freopen("in","r",stdin); //freopen("out","w",stdout); #endif N = read(); for(int i = 1 ; i <= N ; ++i) val.push_back(read()); sort(val.begin() , val.end()); solve(val , 29); cout << sum; return 0; }
CF888G Xor-MST Boruvka、分治、Trie樹合並