P4551 最长异或路径 题解

P4551 最长异或路径

前置知识:01trie

是trie的一种。每个节点是0或者1,我们可以将一些数以二进制表示的形式存入01trie,进而解决一些异或问题。有关trie树的原理再次不在赘述。可上百度自行搜索或阅读有关(oi)书籍。


异或,指一个法则:

两个数的二进制数位上相等即为0,不相等即为1。

显然,一个数对同一个数异或两次等于没有异或,

那么异或也就满足交换律,结合律。

对于一列数的异或和,我们可以将其用结合律从1~i,i+1~j进行异或,然后在将这两个结果进行异或,结果不变。

于是我们可以:

  1. 处理出1到每一个节点的异或和

  2. 把他们加到一棵(01trie)里面

  3. 从高位到低位贪心操作,取最大值,即是答案。

代码:

#include<cstdio>
#include<iostream>
#define N 100007
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
struct edg{
	int next,to,dis;
}edge[N*2];
int t[N<<5][2];
int n,m,u,v,w,num,hea[N],d[N],tot;
inline void add(int from,int to,int dis)
{
	num++;
	edge[num].dis=dis;
	edge[num].to=to;
	edge[num].next=hea[from];
	hea[from]=num;
}
void dfs(int now,int fa)
{
	for(int i=hea[now];i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(v!=fa)
		{
			d[v]=d[now]^edge[i].dis;
			dfs(v,now);
		}
	}
}
void build_trie(int n,int now)
{
	for(int i=30;i>=0;i--)
	{
		int c=(n>>i)&1;
		if(!t[now][c])t[now][c]=++tot;
		now=t[now][c];
	}
}
int query(int n,int now)
{
	int ans=0;
	for(int i=30;i>=0;i--)
	{
		int c=(n>>i)&1;
		if(t[now][c^1])
		{
			ans+=(1<<i),now=t[now][c^1];
		}
		else now=t[now][c];
	}
	return ans;
}
int main(){
	n=read();
	int ans=0;
	for(int i=1;i<n;i++)
	{
		u=read(),v=read(),w=read();
		add(u,v,w);add(v,u,w);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)build_trie(d[i],0);
	for(int i=1;i<=n;i++)
	{
		ans=max(ans,query(d[i],0));
	}
	printf("%d
",ans);
	return 0;
}

完结。

原文地址:https://www.cnblogs.com/lbssxz/p/13267156.html