P4551 最长异或路径 题解
P4551 最长异或路径
前置知识:01trie
是trie的一种。每个节点是0或者1,我们可以将一些数以二进制表示的形式存入01trie,进而解决一些异或问题。有关trie树的原理再次不在赘述。可上百度自行搜索或阅读有关\(oi\)书籍。
异或,指一个法则:
两个数的二进制数位上相等即为0,不相等即为1。
显然,一个数对同一个数异或两次等于没有异或,
那么异或也就满足交换律,结合律。
对于一列数的异或和,我们可以将其用结合律从1i,i+1j进行异或,然后在将这两个结果进行异或,结果不变。
于是我们可以:
-
处理出1到每一个节点的异或和
-
把他们加到一棵\(01trie\)里面
-
从高位到低位贪心操作,取最大值,即是答案。
代码:
#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\n",ans);
return 0;
}
完结。