题解 P4551 【最长异或路径】

题意

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

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

解法

  • 先预处理出从根结点到所有结点的路径上的 \(xor\) 值,显然 \(d(x) = d(fa)\) ^ \(val(fa \to x)\)

  • 又因为异或有一性质 \(a\) ^ \(a=0\) ,所以可以发现从 \(x\)\(y\) 的异或路径就是 \(d(x)\) ^ \(d(y)\),因为 \(d(1)\) ^ \(d(x-1)\) 经过两次计算相抵消了。

  • 然后问题就转化了在 \(1 \le i,j\le n\) 中找到最大的 \(d(i)\) ^ \(d(j)\),把每个数看做一个二进制01串,建立一棵01字典树,利用贪心的思想每次找与当前位相反的结点即可。

代码

#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;
}

时间复杂度为\(\Theta (31*N)\)

posted @ 2020-11-08 15:55  牛蛙丶丶  阅读(54)  评论(0编辑  收藏  举报