2020牛客多校第五場B-Graph
阿新 • • 發佈:2020-09-01
題意
給一棵樹,每條邊有邊權。可以任意加邊和刪邊,但要滿足任何時刻圖連通,而且任何一個環的邊權異或和為0。求操作後最小權值和
題解
任意兩點間連邊的權值是固定的,可以預處理給每個點賦值點權,兩點間的邊權就是點權的異或,點權直接dfs一遍取鏈上的異或和即可。
然後就是異或最小生成樹模板
對於異或最小生成樹,有Boruvka演算法,先對於每個點,選擇在所有與之相連的邊中,權值最小的邊,並將這條邊加入到最小生成樹中。顯然這樣連出來的邊會形成一個森林,並且連邊後連通塊個數至少減半。然後我們將每個連通塊再看成一個點,重複以上演算法即可。時間複雜度O(mlogn)。
對於此題,我們把所有點權扔到Trie裡,對於每一層,有兩種情況,
-
一種全為0,或者全為1,那麼這一位無需考慮,不會對答案產生貢獻。
-
一部分為0,一部分為1,那麼兩組之間要有一條邊把這兩個組相連,在字典樹上找到異或值最小的作為連線兩個集合的邊,加入答案。對兩組分別遞迴求解。
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; struct READ { inline char read() { #ifdef _WIN32 return getchar(); #endif static const int IN_LEN = 1 << 18 | 1; static char buf[IN_LEN], *s, *t; return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++; } template <typename _Tp> inline READ & operator >> (_Tp&x) { static char c11, boo; for(c11 = read(),boo = 0; !isdigit(c11); c11 = read()) { if(c11 == -1) return *this; boo |= c11 == '-'; } for(x = 0; isdigit(c11); c11 = read()) x = x * 10 + (c11 ^ '0'); boo && (x = -x); return *this; } } in; const int N = 1e5 + 50; int a[N]; struct node { int v, w; node(int v = 0, int w = 0): v(v), w(w) {} }; vector<node> G[N]; int cnt = 0; void dfs(int u, int fa) { for (auto nx : G[u]) { if (nx.v == fa) continue; a[nx.v] = a[u] ^ nx.w; dfs(nx.v, u); } } int trie[N << 5][2]; int tot = 0; void ins(int x) { int rt = 0; for (int i = 30; i >= 0; i--) { int now = (x >> i) & 1; if (!trie[rt][now]) trie[rt][now] = ++tot; rt = trie[rt][now]; } } int qry(int x) { int ans = 0, rt = 0; for (int i = 30; i >= 0; i--) { int now = (x >> i) & 1; if (trie[rt][now]) rt = trie[rt][now]; else { rt = trie[rt][now ^ 1]; ans |= (1 << i); } } return ans; } ll ans = 0; void calc(int l, int r, int dep) { if (dep < 0 || l >= r) return; int mid = l - 1; while (mid < r && !((a[mid + 1] >> dep) & 1)) mid++; calc(l, mid, dep - 1); calc(mid + 1, r, dep - 1); if (mid == l - 1 || mid == r) return; tot = 0; for (int i = l; i <= mid; i++) ins(a[i]); int res = 0x7fffffff; for (int i = mid + 1; i <= r; i++) res = min(res, qry(a[i])); ans += res; for (int i = 0; i <= tot; i++) trie[i][0] = trie[i][1] = 0; } int main() { int n; in >> n; for (int i = 1; i < n; i++) { int u, v; ll w; in >> u >> v >> w; u++; v++; G[u].push_back(node(v, w)); G[v].push_back(node(u, w)); } dfs(1, 0); sort(a + 1, a + n + 1); calc(1, n, 30); printf("%lld\n", ans); return 0; }