hdu 4757 Tree 可持续化01字典树+lca

Zero and One are good friends who always have fun with each other. This time, they decide to do something on a tree which is a kind of graph that there is only one path from node to node. First, Zero will give One an tree and every node in this tree has a value. Then, Zero will ask One a series of queries. Each query contains three parameters: x, y, z which mean that he want to know the maximum value produced by z xor each value on the path from node x to node y (include node x, node y). Unfortunately, One has no idea in this question. So he need you to solve it. 
Input 
There are several test cases and the cases end with EOF. For each case:

The first line contains two integers n(1<=n<=10^5) and m(1<=m<=10^5), which are the amount of tree’s nodes and queries, respectively.

The second line contains n integers a1..n1..n and aii(0<=aii<2^{16}) is the value on the ith node.

The next n–1 lines contains two integers u v, which means there is an connection between u and v.

The next m lines contains three integers x y z, which are the parameters of Zero’s query. 
Output 
For each query, output the answer. 
Sample Input 
3 2 
1 2 2 
1 2 
2 3 
1 3 1 
2 3 2 
Sample Output 

0

题目大意:给定一棵树,每个节点有一个值,现在有Q次询问,每次询问u到v路径上节点值与w亦或值的最大值。

因为可持续化01字典树(有点类似主席树)能存下一个区间的所有异或情况,所以,这种多组询问的区间异或值,用可持续化的01字典树比较好。另外因为这种求唯一路径一般都是lca的了。

所以用求lca的时候同时更新字典树就好,基本步骤就是 从树根更新到下面。

然后就说到如何查找了 
那么到了个某个节点能否往某个儿子走的限制条件是 sz[ch[x][c]]+sz[ch[y][c]]-2*sz[ch[z][c]]>0,这样说明下面是存在c的儿子的,接着往下走即可。当然这样算其实是会漏掉lca的,因为这样走在仅仅是走的儿子里面能存在的最大异或值,所以最后还要和lca取最大值。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 1e5 + 5;

int N, Q, E, V[maxn], first[maxn], jump[maxn * 2], link[maxn * 2];
int id, idx[maxn], top[maxn], far[maxn], son[maxn], dep[maxn], cnt[maxn];

inline void add_Edge (int u, int v) {
    link[E] = v;
    jump[E] = first[u];
    first[u] = E++;
}

inline void dfs (int u, int pre, int d) {
    far[u] = pre;
    son[u] = 0;
    dep[u] = d;
    cnt[u] = 1;

    for (int i = first[u]; i + 1; i = jump[i]) {
        int v = link[i];
        if (v == pre) 
            continue;
        dfs(v, u, d + 1);
        cnt[u] += cnt[v];
        if (cnt[son[u]] < cnt[v])
            son[u] = v;
    }
}

inline void dfs (int u, int rot) {
    idx[u] = ++id;
    top[u] = rot;

    if(son[u])
        dfs(son[u], rot);

    for (int i = first[u]; i + 1; i = jump[i]) {
        int v = link[i];
        if (v == far[u] || v == son[u]) 
            continue;
        dfs(v, v);
    }
}

inline int LCA (int u, int v) {
    int p = top[u], q = top[v];
    while (p != q) {
        if (dep[p] < dep[q]) {
            swap(p, q);
            swap(u, v);
        }

        u = far[p];
        p = top[u];
    }
    return dep[u] > dep[v] ? v : u;
}

void init() {
    E = id = 0;
    memset(first, -1, sizeof(first));
    for (int i = 1; i <= N; i++)
        scanf("%d", &V[i]);

    int u, v;
    for (int i = 1; i < N; i++) {
        scanf("%d%d", &u, &v);
        add_Edge(u, v);
        add_Edge(v, u);
    }
    dfs(1, 0, 0);
    dfs(1, 1);
}

struct node {
    int g[2], c;
}nd[maxn * 20];
int sz, root[maxn];

int insert (int r, int w) {
    int ret, x;
    ret = x = sz++;
    nd[x] = nd[r];

    for (int i = 15; i >= 0; i--) {
        int v = (w>>i)&1;
        int t = sz++;
        nd[t] = nd[nd[x].g[v]];
        nd[t].c++;
        nd[x].g[v] = t;
        x = t;
    }
    return ret;
}

void dfs(int u) {
    root[u] = insert(root[far[u]], V[u]);

    for (int i = first[u]; i + 1; i = jump[i]) {
        int v = link[i];
        if (v == far[u]) 
            continue;
        dfs(v);
    }
}

void Tire_init() {
    sz = 1;
    root[0] = nd[0].c = 0;
    memset(nd[0].g, 0, sizeof(nd[0].g));
    dfs(1);
}

int query(int x, int y, int z, int w) {
    int ans = V[z] ^ w, ret = 0;
    z = root[z];
    for (int i = 15; i >= 0; i--) {
        int v = ((w>>i)&1) ^ 1;
        int cnt = nd[nd[x].g[v]].c + nd[nd[y].g[v]].c - 2 * nd[nd[z].g[v]].c;

        if (cnt)
            ret |= (1<<i);
        else
            v = v^1;

        x = nd[x].g[v], y = nd[y].g[v], z = nd[z].g[v];
    }
    return max(ans, ret);
}

int main () {
    while (scanf("%d%d", &N, &Q) == 2) {
        init();
        Tire_init();

        int u, v, w;
        while (Q--) {
            scanf("%d%d%d", &u, &v, &w);
            printf("%d\n", query(root[u], root[v], LCA(u, v), w));
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值