【演算法競賽進階指南】字典樹 The XOR Longest Path
阿新 • • 發佈:2020-11-03
題意
給出一棵有權樹,定義一個路徑的權值為這條路徑上所有邊權的異或和。
請求出最大的路徑權值。
思路
想著想著突然發現從任意一個節點 \(rt\) 開始dfs,對於每個節點 \(u_i\) 可以得到 rt---> \(u_i\) 這條路徑的權值\(val_i\)。
這時我們任意選擇兩個節點 \(u,v\),可以發現 \(val_{u}\ xor\ val_{v}\) 就是 \(u\) 到 \(v\) 的路徑權值。
這時題目就轉化成了,給出 N 個數字求兩個數字的最大異或和。
就跟【演算法競賽進階指南】字典樹 The XOR Largest Pair 一樣了。
程式碼
#include <algorithm> #include <map> #include <queue> #include <stack> #include <stdio.h> #include <string.h> #include <vector> #define pb push_back #define pii pair<int, int> #define pll pair<int, int> typedef long long ll; const int inf = 0x3f3f3f3f; const int N = 2e5 + 10; const int mod = 1e9 + 7; using namespace std; vector<pii> vec[N]; vector<ll> v; void dfs(int u, int fa, ll now) { if (u != fa) v.pb(now); for (int i = 0; i < vec[u].size(); i++) { pll tmp = vec[u][i]; if (tmp.first == fa) continue; dfs(tmp.first, u, now ^ tmp.second); } } int s[N], trie[N * 10][2], tot = 1; void insert() { int p = 1; for (int i = 0; i < 32; i++) { int now = s[i]; if (!trie[p][now]) { trie[p][now] = ++tot; } p = trie[p][now]; } } ll solve() { ll ans = 0, p = 1, cnt = 31; for (int i = 0; i < 32; i++, cnt--) { int now = 1 ^ s[i]; if (trie[p][now]) { ans += (1LL << cnt); } else { now = s[i]; } p = trie[p][now]; } return ans; } int main() { int n; scanf("%d", &n); for (int i = 1; i < n; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); vec[u].pb({ v, w }), vec[v].pb({ u, w }); } dfs(1, 1, 0); ll rel = 0; for (int i = 0; i < v.size(); i++) { for (int j = 0; j < 32; j++) { s[31 - j] = (v[i] & (1LL << j)) ? 1 : 0; } insert(); rel = max(rel, solve()); } printf("%lld\n", rel); return 0; }