The XOR-longest Path 题解

题目传送门

题目大意: 给一棵有边权的树,求树上的最大异或和路径。

题解

需要巧妙地用到异或的一个性质: a   x o r   b = c   ,   c   x o r   b = a a~xor~b=c~,~c~xor~b=a a xor b=c , c xor b=a,即 a   x o r   b   x o r   b = a a~xor~b~xor~b=a a xor b xor b=a

d i s ( x , y ) dis(x,y) dis(x,y) x x x y y y 路径的异或和,那么有: d i s ( x , y ) = d i s ( 1 , x )   x o r   d i s ( 1 , y ) dis(x,y)=dis(1,x)~xor~dis(1,y) dis(x,y)=dis(1,x) xor dis(1,y)

发现所有 d i s ( 1 , i ) dis(1,i) dis(1,i) 是可以 O ( n ) O(n) O(n) 求出来的,问题转化成找到两个异或和最大的 d i s ( 1 , x ) dis(1,x) dis(1,x) d i s ( 1 , y ) dis(1,y) dis(1,y)

考虑贪心,像线性基的求最大异或和类似(如果没学过线性基可以忽略这句话),我们将所有 d i s ( 1 , i ) dis(1,i) dis(1,i) 的二进制数造一棵字典树,然后对于每一个 d i s ( 1 , i ) dis(1,i) dis(1,i),我们都用来求一次答案,从最高位开始贪。因为异或运算的规则是:假如两者的对应位置不一样,那么运算结果的这一位就是 1 1 1,所以我们从最高位开始,尽可能找和当前 d i s ( 1 , i ) dis(1,i) dis(1,i) 的这一位不同的数,假如有,那么就往字典树的那边走,同时答案的这一位变成 1 1 1,否则就往另一边走。

代码如下:

#include <cstdio>
#include <cstring>
#define maxn 100010

struct node{
	node *son[2];
	node(){son[0]=son[1]=NULL;}
};
node *root=new node();
int n;
struct edge{int y,z,next;};
edge e[maxn*2];
int first[maxn];
void buildroad(int x,int y,int z)
{
	static int len=0;
	e[++len]=(edge){y,z,first[x]};
	first[x]=len;
}
int ans=0;
void check(int x)
{
	node *now=root;int sum=0;
	if(root->son[0]==NULL&&root->son[1]==NULL)return;
	for(int i=30;i>=0;i--)
	{
		int p=1;
		if((x&(1<<i))>0)p=0;
		if(now->son[p]!=NULL)sum^=(1<<i),now=now->son[p];
		else now=now->son[p^1];
	}
	if(sum>ans)ans=sum;
}
void add(int x)
{
	node *now=root;
	for(int i=30;i>=0;i--)
	{
		int p=0;
		if((x&(1<<i))>0)p=1;
		if(now->son[p]==NULL)now->son[p]=new node();
		now=now->son[p];
	}
}
void dfs(int x,int fa,int dis)
{
	for(int i=first[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa)continue;
		check(dis^e[i].z);add(dis^e[i].z);
		dfs(y,x,dis^e[i].z);
	}
}

int main()
{
	scanf("%d",&n);
	for(int i=1,x,y,z;i<n;i++)
	scanf("%d %d %d",&x,&y,&z),buildroad(x,y,z),buildroad(y,x,z);
	dfs(1,0,0);
	printf("%d",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值