luoguP4551最长异或路径(01字典树+贪心)

写过字母版的trie树,却不知道还有这等妙用
题目链接
考虑到异或的性质,即异或两次相同的数,结果与原来保持不变,
很容易证明,a ^ b ^ b , 又 b ^ b= 0, 所以 a ^ b ^ b=a ^ 0 = a,所以对于每个节点求一个到根节点的异或路径和dis[v],u->v的异或路径和就很好表示,即为dis[u]^dis[v],如何更新最大又不用n ^ 2枚举呢?

trie数就派上用场在这里插入图片描述
对于每个dis[v]都像这样插入trie树,对于样例即变成这样,

在这里插入图片描述
每次插入时加一个比较操作,即针对已有的节点进行比较,对于一个01串,要想找到与其他串的异或竟可能大,采取贪心策略,即当前位要尽可能不同(0希望对1,1希望对0),从高位开始,逐位比较
贪心策略很好证明.
1000=2^3,0111=2 ^ 3 -1,所以多一位是1其他全是零总比少一位其他全是1大。

直接上代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<algorithm>
#define MAXN 100010
using namespace std;
int dis[MAXN],maxlog=32,cnt,ans,n;
struct Edge{int to,l;};
vector<Edge> E[MAXN];
struct Node{int son[2],f;}N[32*MAXN];
void dfs(int u,int fa,int yh){//维护dis 
	for(int i=0;i<E[u].size();i++){
		int v=E[u][i].to,len=E[u][i].l;
		if(v==fa) continue;
		dis[v]=yh^len;
		dfs(v,u,yh^len);
	}
}
void insert(int u){//插入 
	int now=0;
	for(int i=maxlog-1;i>=0;i--){
		int t=(dis[u]>>i)&1;
		if(!N[now].son[t]) N[now].son[t]=++cnt;
		now=N[now].son[t];
	}
}
int Query(int u){//比较 
	int now=0,sum=0;
	for(int i=maxlog-1;i>=0;i--){
		bool t=(dis[u]>>i)&1;
		if(N[now].son[!t]) sum+=(1<<i),now=N[now].son[!t];
		else now=N[now].son[t];
	} 
	return sum;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		int u,v,w; scanf("%d%d%d",&u,&v,&w);
		E[u].push_back((Edge){v,w});
		E[v].push_back((Edge){u,w});
	}
	dfs(1,0,0); 
	for(int i=1;i<=n;i++){
		insert(i);
		ans=max(ans,Query(i));
	}
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值