POJ 3764 - The xor-longest Path(01字典樹+鏈式向前星)

- POJ 3764 -

The xor-longest Path

Time Limit: 2000MS | Memory Limit: 65536K

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:

xor length(p)=⊕w(e) (e∈p)
⊕ is the xor operator.

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章