codeforces 888G Xor-MST(01字典樹)
阿新 • • 發佈:2019-01-27
這題主要操作就是建立一棵01Trie樹(其實就是一顆普通的二叉樹嘛),由於最大值小於2^30,所以最大樹高到30就好了,在樹的分叉點上,左子樹和右子樹分別是兩個集合(集合的size是葉節點的數量),根分別為A和B,深度(假設葉子節點的深度為1)為h,合併(連線)這兩個集合的時候,需要從這兩個集合選出兩個異或值最小的數。
那麼重點就是如何去選這兩個數。一開始想的是暴力對比,將左右子樹的數字每對都計算一遍異或,時間肯定爆炸了。
方法是選出size較小的那個集合(假設是左子樹的集合),將集合中的每一個數按位從節點B開始往下走,從第h位開始一位一位地往下比對,如果對比到當前節點沒有左節點的話,就走右節點,沒有右節點就往左走,如果左右都有就看當前數字的對比位是0還是1,如果是1就往1的的那個節點走。最後走到葉節點就是當前數在該子樹上異或能取得的最小值。
時間複雜度是樹高(30)乘以節點數量2e5再乘以logn,大約一億左右,兩秒能過
#include<iostream> #include<cstring> #include<string> #include<cstdlib> #include<cstdio> #include<cmath> #include<vector> using namespace std; const int maxn = 2e5 + 5; int n, a; struct Trie { Trie(int H) :h(H), l(NULL), r(NULL) {} vector<int> vec; int h; Trie *l, *r; //l->1, r->0 } T(31); int min(int a, int b) { return a < b ? a : b; } long long solve(Trie *t) { if (t == NULL)return 0; long long ans = 0; Trie *tl = t->l, *tr = t->r, *temp; ans += (solve(tl) + solve(tr)); if (tl && tr) { int m = 0x5f3f3f3f; if (tl->vec.size() < tr->vec.size()) { for (vector<int>::iterator i = tl->vec.begin(); i != tl->vec.end(); i++) { int x = *i; temp = tr; for (int j = t->h - 2; ~j; j--) { if (!temp->l)temp = temp->r; else if (!temp->r) temp = temp->l; else { if (x >> j & 1) temp = temp->l; else temp = temp->r; } } m = min(m, x ^ temp->vec[0]); } } else { for (vector<int>::iterator i = tr->vec.begin(); i != tr->vec.end(); i++) { int x = *i; temp = tl; for (int j = t->h - 2; ~j; j--) { if (!temp->l)temp = temp->r; else if (!temp->r) temp = temp->l; else { if (x >> j & 1) temp = temp->l; else temp = temp->r; } } m = min(m, x ^ temp->vec[0]); } } ans += m; } return ans; } int main() { cin >> n; Trie *temp; while (n--) { temp = &T; cin >> a; for (int j = 30; ~j; j--) { if ((a >> j) & 1) { if (!temp->l)temp->l = new Trie(j); temp = temp->l; } else { if (!temp->r)temp->r = new Trie(j); temp = temp->r; } temp->vec.push_back(a); } } cout << solve(&T) << endl; }