Xor-MST學習/ 2020牛客暑假多校(五)B.Graph
阿新 • • 發佈:2020-08-05
Xor-MST學習/ 2020牛客暑假多校(五)B.Graph
題目大意:
給一個完全圖,求異或最小生成樹。
題解:
首先先看一下這道題:CF888G 。與本題不同之處是這兩個題一個給點的權值,一個給邊的權值。但實際上一樣可以相互轉換。如讓本題中a[1]= 0,跑一下dfs,算出所有點的權值就變為CF888題了。
處理異或,考慮使用Trie樹。Trie樹除了可以處理字串外,還可以處理二進位制。把點值插入Trie樹,會發現,兩個葉子節點的LCA越深,則它們代表的值的異或值越小(LCA相等時情況不一定)。 要使異或值最小就要儘量讓高位相等,儘量走同一條路徑。
所以我們計算每一個子樹的貢獻,考慮啟發式合併,每次做siz小的一邊在字典樹上取計算siz大的一邊的貢獻。複雜度O(nlogn)。總複雜度\(O(nlog^{2}n)\)
其實把CF888G那題當作一個Xor-MST模板題學會,這一題就直接是套路題了
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<map> #include<queue> #include<vector> #include<string> #include<fstream> using namespace std; #define rep(i, a, n) for(int i = a; i <= n; ++ i) #define per(i, a, n) for(int i = n; i >= a; -- i) typedef long long ll; const int N = 5e6 + 105; const int mod = 998244353; const double Pi = acos(- 1.0); const int INF = 0x3f3f3f3f; const int G = 3, Gi = 332748118; ll qpow(ll a, ll b) { ll res = 1; while(b){ if(b & 1) res = (res * a) % mod; a = (a * a) % mod; b >>= 1;} return res; } ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; } // int n; ll a[N]; int head[N], cnt = 0; struct node{ int to, nxt, c; }edge[N]; void add(int u, int v, int w){ edge[cnt].to = v, edge[cnt].c = w, edge[cnt].nxt = head[u], head[u] = cnt ++; edge[cnt].to = u, edge[cnt].c = w, edge[cnt].nxt = head[v], head[v] = cnt ++; } struct XorTrie{ int cnt; int t[N][2]; int L[N], R[N]; void _init(){ cnt = 0; } void Insert(ll x, int id){ int rt = 0; for(int i = 32; i >= 0; -- i){ int op = x >> i & 1ll; if(!t[rt][op]) t[rt][op] = ++ cnt; rt = t[rt][op]; if(!L[rt]) L[rt] = id; R[rt] = id; } } ll ask(int rt, int pos, ll x){ ll res = 0; for(int i = pos; i >= 0; -- i){ int op = x >> i & 1ll; if(t[rt][op]) rt = t[rt][op]; else { rt = t[rt][!op]; res += (1ll << i); } } return res; } ll query(int rt, int pos){ if(t[rt][0] && t[rt][1]){ int x = t[rt][0], y = t[rt][1]; ll minn = 1e18; for(int i = L[x]; i <= R[x]; ++ i) minn = min(minn, ask(y, pos - 1, a[i]) + (1ll << pos)); return minn + query(t[rt][0], pos - 1) + query(t[rt][1], pos - 1); } else if(t[rt][0]) return query(t[rt][0], pos - 1); else if(t[rt][1]) return query(t[rt][1], pos - 1); return 0; } }trie; void dfs(int u, int pre){ for(int i = head[u] ; i != -1; i = edge[i].nxt){ int v = edge[i].to, w = edge[i].c; if(v == pre) continue; a[v] = a[u] ^ w; dfs(v, u); } } int main() { memset(head, -1, sizeof(head)); trie._init(); scanf("%d",&n); for(int i = 1; i < n; ++ i){ int x, y, z; scanf("%d%d%d",&x,&y,&z); x ++; y ++; add(x, y, z); } a[1] = 0; dfs(1, -1); sort(a + 1, a + n + 1); for(int i = 1; i <= n; ++ i) trie.Insert(a[i], i); printf("%lld\n",trie.query(0, 32)); return 0; }