题目链接:https://www.luogu.com.cn/problem/P4551
题目要我们在树上找找一条路径,路径上所有边的边权异或和最大。
思路:
对于每一个点,我们都求它到根节点的异或和,所以我们可以得到n-1个异或和,把这n-1个异或值建01字典树。对于每一个点 i,假设它到根节点的路径的异或和是value[i],只要我们在01字典树中找一个值,和value[i]做异或操作得到的值最大,那么这个最大的值就是点 i 到其他所有点路径上边权异或和的最大值。因为异或同一个数字两次就相当于没有异或,所以即使 i 和另一个点在根节点的同一颗子树上也没有关系,因为公共的那条路径上的边权会异或两次,所以异或值其实就是点 i 到那个点的路径上的异或和。我们只需要枚举每一个点到其他点路径上的异或和最值,取最值中的最大值就可以了。
代码:
#include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<map> #include<stack> #include<cmath> #include<vector> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; typedef long long ll; #define eps 1e-8 #define INF 0x3f3f3f3f #define maxn 100005 struct node{ int v,next,w; }edge[maxn*2]; int head[maxn],L[maxn]; int trie[maxn*35][2]; int n,m,k,t,cnt,step,num; void init(){ memset(head,-1,sizeof(head)); cnt=step=num=0; } void add(int u,int v,int w){ edge[++cnt].v=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt; } void insert(int value){ int root=0; for(int i=31;i>=0;i--){ int c=(value>>i)&1; if(trie[root][c]==0) trie[root][c]=++num; root=trie[root][c]; } } void dfs(int u,int pre,int value){ for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==pre) continue; ++step; //step就是点v的dfs序 L[step]=value^edge[i].w;//暂时存一下v到根节点路径上的异或和 insert(L[step]); //插入字典树 dfs(v,u,L[step]); } } int query(int root,int value,int k){//在01字典树上贪心的找一个数和value做异或操作得到的值最大 if(k<0) return 0; int c=(value>>k)&1; if(trie[root][!c]){ return (1<<k)+query(trie[root][!c],value,k-1); }else{ return query(trie[root][c],value,k-1); } } int main() { scanf("%d",&n); init(); int u,v,w; for(int i=1;i<n;i++){ scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } insert(0);//注意要先给空的字典树插入一个0 dfs(1,-1,0); int ans=0; for(int i=1;i<n;i++){//枚举每一个点到其他点路径的异或和最大值,取最大值中的最大值 ans=max(query(0,L[i],31),ans); } printf("%d ",ans); return 0; }