The xor-longest Path
Description
In an edge-weighted tree, the xor-length of a path p is defined as the xor sum of the weights of edges on p:
We say a path the xor-longest path if it has the largest xor-length. Given an edge-weighted tree with n nodes, can you find the xor-longest path?
Input
The input contains several test cases. The first line of each test case contains an integer n(1<=n<=100000), The following n-1 lines each contains three integers u(0 <= u < n),v(0 <= v < n),w(0 <= w < 2^31), which means there is an edge between node u and v of length w.
Output
For each test case output the xor-length of the xor-longest path.
Sample Input
4
0 1 3
1 2 4
1 3 6
Sample Output
7
Hint
The xor-longest path is 0->1->2, which has length 7 (=3 ⊕ 4)
题意:
在边缘加权树中,路径p的xor长度定义为p上边缘权重的xor和;给定具有n个节点的边缘加权树,找到xor最长的路径。
输入包含几个测试用例。每个测试用例的第一行包含一个整数 n(1 <= n <= 100000),以下 n -1 行每行包含三个整数u(0 <= u < n),v(0 <= v < n) ,w(0 <= w <2 ^ 31),这意味着在节点 u 和 v 之间有一条权值为 w 的边。
分析:
因为是一棵边缘加权树且給定n-1条边,所以肯定不成环,那么就一定有 f(u, v)=f(0, u)^f(0, v)
,即节点 u 和 v 之间边缘权重的异或和为:(节点 u 到根节点 0 的边缘权重异或和) xor (节点 v 到根节点 0 的边缘权重异或和)。
所以先求出每个节点到根节点的边缘权重异或和,然后把它们以二进制的形式存入字典树中,然后就按 01 字典树
的常规解法求出结果。
由于节点数较多,所以不能用邻接矩阵,这里用链式向前星
来求 f (0, u)。
PS:好久以前做的题,现在才来写题解 ̄□ ̄||……咳咳!关于链式向前星,我还是在做这题的时候才第一次接触,至于它的原理,若和我一样是初次接触链式向前星的小白的话,这里有个视频应该可以帮助理解↓
- 链式向前星 -
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
using namespace std;
#define INF 0x3f3f3f
#define zero 1e-7
typedef long long ll;
const int N=1e5+5;
int trie[N*32][2], k;
int head[N<<1], cnt;
int xorr[N];//记录每个节点到根节点的异或值
bool vis[N];//标记节点是否已访问
struct node {
int to, w, next;
}edge[N<<1];//因为是双向边,所以数组要开成两倍
void init() {
cnt=1;//从1开始给边编号
memset(head, -1, sizeof(head));
memset(xorr, 0, sizeof(xorr));
memset(vis, false, sizeof(vis));
k=0;
memset(trie, 0, sizeof(trie));
return ;
}
void addEdge(int u, int v, int w) {
edge[cnt].to=v; edge[cnt].w=w; edge[cnt].next=head[u];
head[u]=cnt++;
//to是和u相连的节点,w是连接节点u和v的边的权值,next记录的是节点u指向v之前所指向的上一个节点的编号
//u指向的所有节点将以邻接表的形式逐个相连,而head记录的是节点u指向的第一个节点的编号
edge[cnt].to=u; edge[cnt].w=w; edge[cnt].next=head[v];
head[v]=cnt++;
return ;
}
void dfs(int u) {
vis[u]=true;
for(int i=head[u]; i!=-1; i=edge[i].next) {//遍历和节点u相连的所有节点
int v=edge[i].to;
if(!vis[v]) {
xorr[v]=xorr[u]^edge[i].w;
dfs(v);
}
}
return ;
}
void inSert(int num) {
int p=0;
for(int i=30; i>=0; i--) {
int c=(num>>i)&1;
if(!trie[p][c])
trie[p][c]=++k;
p=trie[p][c];
}
return ;
}
int Search(int num) {//本来以为能用对链式向前星就行了,没想到栽在这里o(╥﹏╥)o,改了好久,感谢胡萝卜(・ω・)ノ
int p=0, ans=0;
for(int i=30; i>=0; i--) {
int c=(num>>i)&1;
if(trie[p][c^1]) {
p=trie[p][c^1];
if(c^1) ans|=(1<<i);// !!!
}
else {
p=trie[p][c];
if(c) ans|=(1<<i);// !!!
}
}
return ans^num;
}
int main() {
int n, u, v, w;
while(scanf("%d", &n)!=EOF) {
init();
for(int i=0; i<n-1; i++) {
scanf("%d %d %d", &u, &v, &w);
addEdge(u, v, w);
}
dfs(0);
for(int i=0; i<n; i++)
inSert(xorr[i]);
int ans=0;
for(int i=0; i<n; i++) {
//printf("xor[%d]=%d\t", i, xorr[i]);
ans=max(ans, Search(xorr[i]));
}
printf("%d\n", ans);
}
return 0;
}