题意简述:
给定一颗N个节点的树,树上的每一条边都有一个权值。从数中选择两个点x和y,把从x到y的路径上的所有边权xor(异或)起来,得到的结果最大是多少?
解题思路:
假设D[x]为从根节点到节点x路径权值的xor,那么很显然D[x] = D[father[x] ]。也就是说我们可以从树的根节点出发,通过一边dfs求出根到每个节点的D。而由于异或的特性,我们知道从节点x到节点y路径权值的xor,就是D[x] ^ D[y]。因为a^a = 0,所以“从根到节点x的路径权值xor” 与“从根到节点y的路径权值xor”刚好抵消。
于是我们就可以先通过一遍dfs求出每个节点的D,然后通过字典树来从这N个D中选出两个xor最大的,并输出。
注意事项:
首先数据范围很大,肯定不能用cin等慢读入。然后就是vector也很耗费时间,所以我们采用数组模拟树结构,采用“儿子+兄弟们”结构存树。还有就是别忘了怎么找父亲节点,这里采用将父节点的D通过参数传递。
代码示例:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 100100;
int t[MAXN*33][3],tot = 1,D[MAXN];
int head[MAXN],cnt = 1;
bool vis[MAXN];
//因为用vector会超时,所以我们采用数组来存贮树结构,儿子+兄弟们
struct Edge{
int to,val,brh;
Edge(){}
Edge(int to,int val,int brh):to(to),val(val),brh(brh){}
}edges[MAXN*4];
//添加一条有向边,添加两条就是无向边了
void addEdge(int from,int to,int val){
edges[cnt] = Edge(to,val,head[from]);
head[from] = cnt++;
}
//要先dfs一次来求从根节点到该节点路径权值的xor和
void dfs(int p,int ww){
//这里要把当前父节点xor和作为参数ww传递,因为对于每条边,我们没有记录父节点
vis[p] = true;
D[p] = ww;
for(int i = head[p];i != -1;i = edges[i].brh){
if(!vis[edges[i].to])
dfs(edges[i].to,ww^edges[i].val);
}
}
//以下是字典树求两个数最大异或和代码
void insert(int x){
int p = 1;
for(int k = 30;k >= 0;k--){
int ch = x>>k&1;
if(t[p][ch] == 0) t[p][ch] = ++tot;
p = t[p][ch];
}
}
int search(int x){
int p = 1,ans = 0;
for(int k = 30;k >= 0;k--){
int ch = x>>k&1;
if(t[p][!ch]){
p = t[p][!ch];
ans |= 1<<k;
}else p = t[p][ch];
}
return ans;
}
//初始化函数,用于多组测试用例
void init(){
memset(head,-1,sizeof head);
memset(vis,false,sizeof vis);
memset(t,0,sizeof t);
memset(D,0,sizeof D);
cnt = tot = 1;
}
int main(){
int n,u,v,w;
while(scanf("%d",&n) != EOF){
int ans = 0;init();
for(int i = 1;i < n;i++){
scanf("%d%d%d",&u,&v,&w);
addEdge(u,v,w);
addEdge(v,u,w);
}
dfs(0,0);//假设根节点为0
for(int i = 0;i < n;i++){
insert(D[i]);
ans = max(ans,search(D[i]));
}
printf("%d\n",ans);
}
return 0;
}
参考书目:
- 《算法竞赛进阶指南》李煜东.P73