子树内外最大异或和 | 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;
}
posted @ 2025-07-11 11:03  心灵震荡  阅读(16)  评论(0)    收藏  举报