P4551 最长异或路径 - 洛谷

题意

给你一棵 带权树(每个边有一个权值),让你找到树上任意两个节点之间的路径,使得这条路径上所有边权的异或和(XOR)最大,并输出这个最大值。

思路

树上任意两点 u 和 v 之间路径的异或和,等于 u 到根节点的异或和 异或上 v 到根节点的异或和

Why?! 
因为从根节点到 u,和从根节点到 v 的路径中,重复的部分(即从根节点到它们最近公共祖先 LCA 的那一段)会被异或两次。而根据异或的性质 a ^ a = 0,这一段就抵消掉了,剩下的正好是 u 到 v 的唯一路径。

  • 操作:任选一个根(比如节点 1),跑一遍 DFS,计算出每个节点到根的异或值,存入数组 dis 中。

  • 结果:问题成功从“树上找两条路径”降维成了“在数组 dis 中找两个数,使它们的异或值最大”。

转化后的经典问题(最大异或对)P10471 最大异或对 The XOR Largest Pair - 洛谷

现在你手里有一个数组 dis,里面全是整数。你需要找出 dis[i] ^ dis[j] 的最大值。
如果暴力枚举所有数对(O(n^2)),对于 n=100000 的数据必然超时。

接下来我们就需要利用 01-Trie 进行贪心查找

01-Trie 本质上是一棵二叉树,每个节点只有 0 和 1 两个分叉,用来存储数字的二进制位。

  1. 插入(建树):将数组 d 中的每个数字,从**高位(第 30 位)到低位(第 0 位)**依次插入 Trie 树中。

  2. 查询(贪心):遍历数组 d 中的每个数字 x,在 Trie 树中查找与它异或结果最大的那个数。

    • 贪心策略:为了让异或结果最大,高位优先要变成 1。所以对于 x 的当前位 bit,我们优先走 bit ^ 1 的分支(相反方向)。

    • 如果有相反分支,说明这一位异或结果是 1,累加到答案中,走过去。

    • 如果没有相反分支,只能走相同的分支,这一位异或结果是 0,走过去。

  3. 记录每次查询结果的最大值,就是最终答案。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5,maxm=1e5*32+5;
struct Trie01{
int ch[maxm][2],tot;
void init(){
for(int i=0;i<=tot;++i) ch[i][0]=ch[i][1]=0;
tot=0;
}
void insert(int x){
int u=0;
for(int i=30;i>=0;--i)
{
int v=(x>>i)&1;
if(!ch[u][v]) ch[u][v]=++tot;
u=ch[u][v];
}
}
int query(int x){
int u=0,res=0;
for(int i=30;i>=0;--i)
{
int v=(x>>i)&1;
if(ch[u][v^1])
{
res|=(1<<i);
u=ch[u][v^1];
}else u=ch[u][v];
}
return res;
}
}trie01;
struct Edge{
int v;ll w;
};
vector<Edge> adj[maxn];
int dis[maxn];//每个点到根的距离的异或和
int n;
void dfs(int u,int p)
{
for(auto [v,w]:adj[u])
{
if(v==p) continue;
dis[v]=dis[u]^w;
dfs(v,u);
}
}
void solve()
{
cin>>n;
for(int i=0;i<n-1;++i)
{
int u,v,w;
cin>>u>>v>>w;
adj[u].push_back({v,w});
adj[v].push_back({u,w});
}
dfs(1,0);
trie01.init();
for(int i=1;i<=n;++i) trie01.insert(dis[i]);
int ans=0;
for(int i=1;i<=n;++i)
{
ans=max(ans,trie01.query(dis[i]));
}
cout<<ans<<"\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int ___T=1;
//cin>>___T;
while(___T--) solve();
return 0;
}