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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值