POJ_3764_The xor-longest Path(01字典树)

题型:数据结构


题意:

树有n个结点,每条边上有一个权值。

u到v的路径值,为从u到v的路径上所有边的权值的异或值。

求所有路径中的最大值。


分析:

对于许多个异或值里取最值的处理,字典树一直都是个好方法。

根据异或的一个性质,a⊕b = (a⊕c)(c⊕b)

我们可以预处理出每一个节点到根节点的路径值,这个用dfs解决。

这样我们可以求出任意两个点的路径值。

但是有n个值,暴力求最值,O(n^2)复杂度过高。

采用字典树处理。

将这n个值转换为32位二进制串插入01字典树。

然后遍历n个数,查找每个数在字典树中能够异或出的最大值。

处理方法为,从高位开始,每次都尽量查找相反的值,是0就查1,是1就查0,查不到的话就查原值。因为高位是尽可能为1,所以就能保证查找出来的异或值最大。

最后选一个最大值为答案。


代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>

#define mt(a,b) memset(a,b,sizeof(a))
using namespace std;
const int M = 32*100000+10;
int n;

struct G {
    struct E {
        int u,v,w,next;
    } e[M];
    int le,head[M];
    void init() {
        le = 0;
        mt(head,-1);
    }
    void add(int u,int v,int w) {
        e[le].u = u;
        e[le].v = v;
        e[le].w = w;
        e[le].next = head[u];
        head[u] = le++;
    }
} g;

int val[100010];
int fa[100010];
void dfs(int u) {
    for(int i=g.head[u]; ~i; i=g.e[i].next) {
        int v = g.e[i].v;
        if(v != fa[u]) {
            fa[v] = u;
            val[v] = val[u] ^ g.e[i].w;
            dfs(v);
        }
    }
}

class Trie_tree { //字典树 前缀树
    struct node {
        int next[2],ptr,num;// ptr在字典中的位置,num此结点出现几次,
        void init() {
            ptr=-1;
            num=0;
            mt(next,0);
        }
    } s[M];
    int ls,p;
public:
    int ch[33];
    int tmp[33];
    void init() {
        s[1].init();
        p=0;
        ls=2;
    }

    void toer(int x) {
        mt(ch,0);
        int pre = 0;
        while(x) {
            ch[pre] = x%2;
            pre++;
            x /= 2;
        }
        for(int i=pre; i<32; i++) {
            ch[i] = 0;
        }
        for(int i=0,j=31; i<j; i++,j--) {
            ch[i] ^= ch[j];
            ch[j] ^= ch[i];
            ch[i] ^= ch[j];
        }
    }

    void INSERT(int x) {
        toer(x);
        int head = 1;
        for(int k=0; k<32; k++) {
            int id = ch[k];
            if(!s[head].next[id]) {
                s[head].next[id] = ls;
                s[ls++].init();
            }
            head = s[head].next[id];
            s[head].num ++;
        }
        s[head].ptr=p++;
    }

    int QUERY(int x) {
        toer(x);

        for(int i=0; i<32; i++) {
            if(ch[i]) ch[i] = 0;
            else      ch[i] = 1;
        }

        mt(tmp,0);
        int head = 1;
        for(int k=0; k<32; k++) {
            if(s[head].next[ch[k]]) {
                tmp[k] = 1;
                head = s[head].next[ch[k]];
            } else {
                tmp[k] = 0;
                head = s[head].next[(ch[k]+1)%2];
            }
        }

//        for(int i=0;i<32;i++){
//            printf("%d ",tmp[i]);
//        }
//        puts("");

        int sum = 0;
        int mul = 1;
        for(int i=31; i>=0; i--) {
            if(tmp[i]) {
                sum += mul;
            }
            mul<<=1;
        }
//        printf("%d\n",sum);
        return sum;
    }
} ts;

int main() {
    while(~scanf("%d",&n)) {
        int u,v,w;
        g.init();
        for(int i=0; i<n-1; i++) {
            scanf("%d%d%d",&u,&v,&w);
            g.add(u,v,w);
            g.add(v,u,w);
        }

        mt(val,0);
        mt(fa,-1);
        dfs(0);

//        for(int i=0;i<n;i++){
//            printf("%d\n",val[i]);
//        }

        ts.init();

        for(int i=0; i<n; i++) {
            ts.INSERT(val[i]);
        }

        int maxn = -1;
        for(int i=0; i<n; i++) {
            maxn = max(maxn,ts.QUERY(val[i]));
        }

        printf("%d\n",maxn);

    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值