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;
}