子树内外最大异或和 | P6072 『MdOI R1』Path
原题链接:https://www.luogu.com.cn/problem/P6072
容易发现,问题等价于对每个点找到子树内外的最大异或和,并对它们的和取 max.
子树内的情况,可以树剖之后启发式合并。具体地,对每个节点维护一个 trie. 先处理完所有轻子树内的情况,然后处理重子树的情况并继承重子树的 trie,然后将轻子树中所有节点插入到 trie 中。容易发现,每个数只会向上插至多 \(\log n\) 次,所以复杂度为 \(O(n \log n \log V)\).
子树外的情况,考虑先找出整棵树中最大的异或和 \(T = a_x \oplus a_y\). 注意到对任何不在 \(1 \to x\) 与 \(1 \to y\) 路径上的节点,它们的答案均为 \(T\),于是只需计算这两条链上的答案。
从 \(1\) 开始 dfs,对每个节点,不断将其不在路径上的子节点插入一棵 trie 即可。由于插入的总次数至多为 \(2n\),所以这部分时间复杂度为 \(O(n \log V)\).
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5;
int n, u, v, w, head[N], tot, a[N], ans, f[N], g[N], heavy[N], siz[N], X, Y, fth[N];
bool vis[2][N];
struct edge
{
int to, nxt, val;
} e[N << 2];
void addedge(int u, int v, int w)
{
e[++tot] = {v, head[u], w};
head[u] = tot;
}
void dfs(int u, int fa)
{
siz[u] = 1, fth[u] = fa;
for(int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if(v == fa) continue;
a[v] = a[u] ^ e[i].val;
dfs(v, u);
siz[u] += siz[v];
if(siz[v] > siz[heavy[u]]) heavy[u] = v;
}
return;
}
struct TRIE
{
int nxt[N * 32][2], top;
void build()
{
for(int i = 0; i <= top; i++)
nxt[i][0] = nxt[i][1] = 0;
top = 0;
return;
}
void insert(int x)
{
int p = 0;
for(int i = 30; i >= 0; i--)
{
int o = (x >> i) & 1;
if(!nxt[p][o]) nxt[p][o] = ++top;
p = nxt[p][o];
}
return;
}
int query(int x)
{
int p = 0, res = 0;
for(int i = 30; i >= 0; i--)
{
int o = (x >> i) & 1;
if(nxt[p][o ^ 1]) res += (1 << i), p = nxt[p][o ^ 1];
else p = nxt[p][o];
}
return res;
}
} trie;
int getlig(int u, int fa)
{
int res = trie.query(a[u]);
trie.insert(a[u]);
for(int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if(v != fa) res = max(res, getlig(v, u));
}
return res;
}
void dfs2(int u, int fa)
{
if(!heavy[u])
{
f[u] = 0;
trie.build();
trie.insert(a[u]);
return;
}
for(int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if(v == fa || v == heavy[u]) continue;
trie.build();
dfs2(v, u);
f[u] = max(f[u], f[v]);
}
trie.build();
dfs2(heavy[u], u);
f[u] = max(f[heavy[u]], f[u]);
f[u] = max(f[u], trie.query(a[u]));
trie.insert(a[u]);
for(int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if(v == fa || v == heavy[u]) continue;
f[u] = max(f[u], getlig(v, u));
}
return;
}
void dfs3(int o, int u, int fa)
{
if(fa)
{
g[u] = max(g[u], g[fa]);
g[u] = max(g[u], trie.query(a[fa]));
trie.insert(a[fa]);
for(int i = head[fa]; i; i = e[i].nxt)
{
int v = e[i].to;
if(vis[o][v] || v == fth[fa]) continue;
g[u] = max(g[u], getlig(v, fa));
}
}
for(int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if(!vis[o][v] || v == fa) continue;
dfs3(o, v, u);
}
return;
}
int main()
{
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i < n; i++)
{
cin >> u >> v >> w;
addedge(u, v, w);
addedge(v, u, w);
}
dfs(1, 0);
dfs2(1, 0);
trie.build();
for(int i = 1; i <= n; i++)
{
int qk = trie.query(a[i]);
if(qk > g[0]) X = i, g[0] = qk;
trie.insert(a[i]);
}
for(int i = 1; i <= n; i++)
if((a[X] ^ a[i]) == g[0]) Y = i;
int x = X, y = Y;
while(x) vis[0][x] = 1, x = fth[x];
while(y) vis[1][y] = 1, y = fth[y];
for(int i = 1; i <= n; i++)
if(!vis[0][i] && !vis[1][i]) g[i] = g[0];
trie.build();
dfs3(0, 1, 0);
trie.build();
dfs3(1, 1, 0);
for(int i = 2; i <= n; i++)
ans = max(ans, f[i] + g[i]);
cout << ans << '\n';
return 0;
}
望穿寂夜晨曦至,雄鹰展翅图九天。

浙公网安备 33010602011771号