题意:已知:给出n个结点的树,定义:两结点间的权值为两点之间所有边相异或的值.求:树中的某两点间的最大权值.
思路:先说简单一点的题:有道CowXor,是一串线性序列,求某连续段异或的最大值,这题的思路是先求前i项序列相异或的值Si,所以x到y的连续异或就是Sx^Sy ,因为a^b = (a ^ c) ^ (b ^ c).
这题同样是这个思路把线性拓展到树上,先求任何点到某一定点的连续异或值,比如选根结点0,所以这时候有两种情况,1.x,y的路径通过了根结点,显然正确。2.x,y的路径不通过根结点,可以知道a^b = (a ^ c) ^ (b ^ c). c就是他们到根结点的共同的路径,同样也正确。
这是第一大步,第二,求出以后不能直接暴力枚举所有两点,n^2 的复杂度会T,可以用字典树把复杂度降到几乎线性
第一大步已经求出了Si,再从i=1 to n,再字典树上插入Si,再求出当前情况下,路径权的最大值,也就是从根结点往下走,有与当前位相异的值就累加,这点自己想一下即可。
代码:
//15560K 735MS
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N= 1e5+100;
int n,ans;
struct edge{
int to;
int next;
int w;
}es[N<<1];
bool vis[N];
int head[N];
inline void add_edge(int u,int v,int w,int i){
es[i].to=v;
es[i].w=w;
es[i].next=head[u];
head[u]=i;
}
int XOR[N];
void dfs(int u){
vis[u]=1;
for(int k=head[u];k!=-1;k=es[k].next){
if(vis[es[k].to]) continue;
else XOR[es[k].to]=XOR[u]^es[k].w;
dfs(es[k].to);
}
}
struct Trie{
int child[2];
void ini(){
child[0]=0;
child[1]=0;
}
}trie[N<<8];
int L;
void Insert(int val){
int cur=0;
for(int i=30;i>=0;i--){
int m=val&(1<<i)? 1:0;
if(!trie[cur].child[m]){
trie[cur].child[m]=++L;
trie[L].ini();
}
cur=trie[cur].child[m];
}
}
int Find(int val){
int ans=0;
int cur=0;
for(int i=30;i>=0;i--){
int g=val&(1<<i)? 0:1;
if(trie[cur].child[g]){
ans|=1<<i;
cur=trie[cur].child[g];
}
else {
cur=trie[cur].child[!g];
}
}
return ans;
}
void ini(){
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
ans=-1;
trie[0].ini();
L=0;
}
int main(){
while(~scanf("%d",&n)){
ini();
for(int i=1;i<=n-1;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w,i);
add_edge(v,u,w,i+n-1);
}
dfs(0);
for(int u=0;u<n;u++){
Insert(XOR[u]);
int t=Find(XOR[u]);
ans=max(ans,t);
}
printf("%d\n",ans);
}
return 0;
}
开始用动态分配字典树空间T了,然后用了静态wa了....我误认为数据中0就是根结点,1...n依次根据深度编号..但是1...n不是根据深度编号的也就是随便编号的...
比如2结点可能连在10结点上,10结点却连在0号根结点上
所以由原来的添边add_edge(min(v,u),max(v,u),w,i);
改成了add_edge(u,v,w,i); add_edge(v,u,w,i+n-1);双向边,就ac了