01Trie树--P4551 最长异或路径
一个定理:$a$-$root$的异或和^$b$-$root$的异或和=$a$-$b$的异或和
证明:$a$^$a_1$^$a_2$^……$root$^$root$^$b$^$b_1$^$b_2$^……
因为:$a$^$a$=0 0异或任何数为它本身,所以上述式子就等于$a$^$a_1$^$a_2$^……^$b$^$b_1$^$b_2$^……,就等于$a$-$b$的异或和
因此求两个点之间的路径的异或和=两个点到根节点的异或和的异或值,这两个值我们可以预处理维护出来
拆分成二进制后,从高位往低位加入Trie树中,枚举依次从$n$个点出发,能得到的最大的路径异或和
如何求最大的路径异或和???
如果高位为1,肯定比低位为1更优,所以我在Trie树中往下找的时候,最优的策略是本位0取1,本位1取0,如果不能取的话,那就……不取吧
代码实现:
1、求每个点到根节点的异或和
1 void dfs(int now,int fa){
2 for (int i = head[now];i;i=ed[i].next){
3 int to=ed[i].to;
4 if (to==fa) continue;
5 dis[to]=dis[now]^ed[i].w;
6 dfs(to,now);
7 }
8 }
2、建Trie树: $x$&$i$可以求出$x$这个数拆分成2进制后,第$i$位是不是1
1 void build(int x,int root){
2 for (int i = (1<<30);i;i>>=1){
3 bool c=x&i;
4 if (!trie[root][c]) trie[root][c]=++cnt;
5 root=trie[root][c];
6 }
7 }
3、查询: 本位$c$取$c^1$,如果可取,跳到$c^1$,否则只能跳到$c$
1 int query(int root,int x){
2 int ans=0;
3 for(int i = (1<<30);i;i>>=1){
4 bool c=x&i;
5 if (trie[root][1^c]) ans+=i,root=trie[root][1^c];
6 else root=trie[root][c];
7 }
8 return ans;
9 }
完整代码:
1 #include <iostream>
2 #include <algorithm>
3 #include <cstdio>
4 using namespace std;
5 int n;
6 const int maxn=1e6+10;
7 int trie[maxn][5],head[maxn],tot,dis[maxn],cnt;
8 struct node{
9 int next,to,w;
10 }ed[maxn];
11 void add(int u,int to,int w){
12 ed[++tot].to=to;
13 ed[tot].w=w;
14 ed[tot].next=head[u];
15 head[u]=tot;
16 }
17 void dfs(int now,int fa){
18 for (int i = head[now];i;i=ed[i].next){
19 int to=ed[i].to;
20 if (to==fa) continue;
21 dis[to]=dis[now]^ed[i].w;
22 dfs(to,now);
23 }
24 }
25 void build(int x,int root){
26 for (int i = (1<<30);i;i>>=1){
27 bool c=x&i;
28 if (!trie[root][c]) trie[root][c]=++cnt;
29 root=trie[root][c];
30 }
31 }
32 int query(int root,int x){
33 int ans=0;
34 for(int i = (1<<30);i;i>>=1){
35 bool c=x&i;
36 if (trie[root][1^c]) ans+=i,root=trie[root][1^c];
37 else root=trie[root][c];
38 }
39 return ans;
40 }
41 int u,v,w;
42 int main(){
43 scanf ("%d",&n);
44 for (int i = 1;i <= n-1;i++){
45 scanf ("%d%d%d",&u,&v,&w);
46 add(u,v,w);add(v,u,w);
47 }
48 dfs(1,0);
49 int ans=0;
50 for (int i = 1;i <= n;i++) build(dis[i],0);
51 for (int i = 1;i <= n;i++){
52 ans=max(ans,query(0,dis[i]));
53 }
54 printf("%d\n",ans);
55 return 0;
56 }