题目背景
OURCE:NOIP2015-SHY-7
题目描述
求一棵带边权的树的一条最大 Xor 路径的值。这里的“路径”不一定从根到叶子结点,中间一段路径只要满足条件也可以。
输入格式
第一行,一个整数 N ,表示一颗树有 N 个节点,接下来 N-1 行,每行三个整数 a,b,c 表示节点 a 和节点 b 之间有条权值为 c 的边。
输出格式
输出仅一行,即所求的最大值。
样例数据
输入
4
1 2 3
1 3 4
1 4 7
输出
7
备注
【数据范围】
对 40% 的输入数据 :数据退化为一条链;
另对 10% 的输入数据 :N≤1000;
对 100% 的输入数据 :1≤N≤100000, c≤
231
-1。
分析:这种题都是套路(好像以前也做过就是忘了)。由异或的性质,任意两点的路径的异或和就是这两点到根节点的异或和相异或。所以只需求出每个节点到根节点的异或和,丢进一棵trie树,再一个一个在trie树中寻找有没有与它相反的二进制数(比如,现在你枚举到101,就应该在trie树中找到010来异或成111。当然,如果最长二进制数有10位,你应该找1111111010来异或成1111111111),与ans比较一下,取较大值,得出答案。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
const int N=100010,L=35;
struct node
{
int son[2];
}tree[N*L];
int n,len,ans,x,y,z;
int tot,first[N],nxt[N<<1],to[N<<1],w[N<<1];
int dep[N];
void addedge(int x,int y,int z)
{
tot++;
nxt[tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
tot++;
nxt[tot]=first[y];
first[y]=tot;
to[tot]=x;
w[tot]=z;
}
void dfs(int u,int fa)
{
for(int p=first[u];p;p=nxt[p])
{
int v=to[p];
if(v!=fa)
{
dep[v]=dep[u]^w[p];
dfs(v,u);
}
}
}
void buildtree(int val)
{
int pos=0;
for(int i=len-1;i>=0;--i)
{
int t=(val>>i)&1;
if(!tree[pos].son[t])
tree[pos].son[t]=++tot;
pos=tree[pos].son[t];
}
}
int find(int val)
{
int pos=0;
for(int i=len-1;i>=0;--i)
{
int t=(val>>i)&1;
if(tree[pos].son[t^1])//如果能找到相反的二进制数
pos=tree[pos].son[t^1],val=val|(1<<i);//或运算的意思是val那一位直接改成1,因为已经确定了能异或成1
else
pos=tree[pos].son[t],val=val^(t<<i);//不能找到就只好委曲求全走和自己相同的,那一位改成0
}
return val;
}
int main()
{
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
n=getint();
for(int i=1;i<n;++i)
{
x=getint(),y=getint(),z=getint();
ans=max(ans,z);
addedge(x,y,z);
}
dfs(1,0);//求根节点到每个节点的异或和
while(ans)
ans>>=1,len++;//计算最长的二进制数来确定trie树深度
tot=0;
for(int i=1;i<=n;++i)
buildtree(dep[i]);//建trie树
for(int i=1;i<=n;++i)//到trie树中查找
{
dep[i]=find(dep[i]);
ans=max(ans,dep[i]);
}
cout<<ans<<'\n';
return 0;
}
本题结。