#include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/43486733"); }
题意:
多组数据、
给你一颗树,
然后求一条最长异或路径,
异或路径长度定义为两点间简单路径上所有边权的异或和。
题解:
首先无根树转有根树再在树上跑一遍算出每个点到根的异或和。
然后两点间异或路径长度就是a[i]*a[j]。
因为lca之前那一段都被异或了两次搞没了。
然后求个线性基,然后随便搞搞就可以?可以WA了!
因为那么算哪是简单路径啊,或者说,那特喵的是什么玩意啊。
嗯,写个Trie树,然后详情可以看我这篇博客。
【JDFZOJ】最富有的人 Trie树+异或性质
诶?这篇博客什么也没说?
好吧,我补个题解:
Trie里面记录每个节点权值的二进制串,
比如5,我们不记录“101”,而是记录“00000……000101”
这样Trie树建好了以后我们枚举一个节点,然后用这个节点在Trie树中找它的最长异或路径长度。
这个时候我们在Trie树上跑,每一位都取反以保证这位尽量是1
然后走不了再取同。
所有最长中取个最大值作为答案
这样就在nlogn时间内得到了解。
详见代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 101000 #define LOGN 32 #define T 2 #define inf 0x3f3f3f3f using namespace std; int src[N],n; struct Trie { int son[N*LOGN][T],cnt; void init() { memset(son,0,sizeof son); cnt=0; } void insert(int a) { int i,x=0,alp; for(i=31;i>=0;i--) { alp=(a>>i)&1; if(!son[x][alp])son[x][alp]=++cnt; x=son[x][alp]; } } int find(int a) { int i,x=0,alp; int ret=0; for(i=31;i>=0;i--) { alp=!((a>>i)&1); ret<<=1; if(son[x][alp]) { x=son[x][alp]; ret++; } else x=son[x][!alp]; } return ret; } }trie; struct KSD { int v,len,next; }e[N<<1]; int head[N],cnt; void init() { cnt=0; memset(src,0,sizeof src); memset(head,0,sizeof head); trie.init(); } inline void add(int u,int v,int len) { e[++cnt].v=v; e[cnt].len=len; e[cnt].next=head[u]; head[u]=cnt; } void dfs(int x,int p) { int i,v; for(i=head[x];i;i=e[i].next) { v=e[i].v; if(v==p)continue; src[v]=src[x]^e[i].len; dfs(v,x); } return ; } int main() { freopen("test.in","r",stdin); int i,j,k; int a,b,c; while(scanf("%d",&n)!=EOF) { init(); for(i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); add(a+1,b+1,c),add(b+1,a+1,c); } dfs(1,0); int ans=0; for(i=1;i<=n;i++)trie.insert(src[i]); for(i=1;i<=n;i++)ans=max(ans,trie.find(src[i])); printf("%d\n",ans); } return 0; }