【Trie树】P4551 最长异或路径

103 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第20天,点击查看活动详情

最长异或路径

题目描述

给定一棵 nn 个点的带权树,结点下标从 11 开始到 nn。寻找树中找两个结点,求最长的异或路径。

异或路径指的是指两个结点之间唯一路径上的所有边权的异或。

输入格式

第一行一个整数 nn,表示点数。

接下来 n1n-1 行,给出 u,v,wu,v,w ,分别表示树上的 uu 点和 vv 点有连边,边的权值是 ww

输出格式

一行,一个整数表示答案。

样例 #1

样例输入 #1

4
1 2 3
2 3 4
2 4 6

样例输出 #1

7

提示

最长异或序列是 1,2,31,2,3,答案是 7=347=3\oplus 4

数据范围

1n100000;0<u,vn;0w<2311\le n \le 100000;0 < u,v \le n;0 \le w < 2^{31}

题目描述

给定一棵 n 个点的带权树,结点下标从 1 开始到 N 。寻找树中找两个结点,求最长的异或路径。

异或路径指的是指两个结点之间唯一路径上的所有边权的异或。

思路

首先强推一下01字典树(Trie),这个东西是解决xor问题的利器.

查找最大异或值的时候我们一般从最高位到低位向下找

   eg:   1000(2)=8(10)
         0111(2)=7(10)

显然只要我的最高位是1,除非你和我的最高位相同,要不然我就是比你大.

根据数学上的等比数列求和可知 8=2^3 ,7=2^3-1

所以说我们可以贪心的去找当前位^1的节点

01字典树的写法和trie树差不多,对于这个题,

过程:
     1.建图跑一下再去dfs去求每个节点到根节点的xor值。
     2.再去构建01Trie去实现我们的贪心即可
#include <iostream>
#include <algorithm>
#define M 200070
#define N 5000070

using namespace std;

struct Edge
{
	int to,nxt,w;
}e[M];
int n,ans,cnt,tot;
int d[M],head[M],nxt[N][2];

void add(int from,int to,int val){
	e[++cnt].to=to;
	e[cnt].w=val;
	e[cnt].nxt=head[from];
	head[from]=cnt;
}

void dfs(int x,int fa){
	for(int i=head[x];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa) continue;
		d[v]=d[x]^e[i].w;
		dfs(v,x);
	}
}

void insert(int x){
	int c=0,op;
	for(int i=31;i>=0;--i){
		op=((x>>i)&1);
		if(!nxt[c][op])
			nxt[c][op]=++tot;
		c=nxt[c][op];
	}
}

int query(int x){
	int c=0,sum=0,op;
	for(int i=31;i>=0;--i){
		op=((x>>i)&1);
		if(nxt[c][op^1])
			c=nxt[c][op^1],sum=sum<<1|1;
		else
			c=nxt[c][op],sum=sum<<1;
	}
	return sum;
}

int main(){
	cin>>n;
	for(int i=1;i<n;++i){
		int u,v,w;
		cin>>u>>v>>w;
		add(u,v,w);
		add(v,u,w);
	}
	dfs(1,0);
	insert(d[1]);
	for(int i=2;i<=n;++i){
		ans=max(ans,query(d[i]));
		insert(d[i]);
	}
	cout<<ans<<endl;
}