HDU 6191 DFS序+可持久化字典树

题目链接


思路:
推荐两个前置的入门题:题目1 题目2

理解上面两题之后此题就变得很套路了。

先用 D F S DFS DFS序转树形结构为线性结构,然后按 D F S DFS DFS序的顺序向字典树中插值,维护一个前缀字典树,然后利用可持久化保留每一个插值前的字典树历史版本。

因为一个以 u u u节点为根的子树的所有节点在 D F S DFS DFS序中连续,且为 i n [ u ] − o u t [ u ] in[u]-out[u] in[u]out[u]

故利用差分的思想,可实现高效查询。
注意特判子树根的异或和。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int A = 2e5 + 10;
class Gra{
public:
    int v,next;
}G[A<<2];
class Trie{
public:
    int ne[2],cnt;
}T[A<<5];
int head[A],val[A],in[A],out[A],id[A],root[A];
int tot,twt,n,q;

void init(){
    tot = twt = root[0] = T[0].cnt = 0;
    memset(T[0].ne,0,sizeof(T[0].ne));
    memset(head,-1,sizeof(head));
}

void add(int u,int v){ G[tot].v = v;G[tot].next = head[u];head[u] = tot++;}

void dfs(int u,int pre){
    in[u] = ++twt;
    id[twt] = u;
    for(int i = head[u] ;i!=-1 ;i=G[i].next){
        int v = G[i].v;
        if(v == pre) continue;
        dfs(v,u);
    }
    out[u] = twt;
}

int insert(ll v,int y){
    int x,res;
    x = res = ++tot;T[x] = T[y];
    for(int i=32 ;i>=0 ;i--){
        int c = (v>>i)&1;
        int tem = ++tot;
        T[tem] = T[T[x].ne[c]];
        T[tem].cnt++;
        T[x].ne[c] = tem;
        x = tem;
    }
    return res;
}

ll query(int u,ll v){
    ll ans = v^val[u],res = 0;
    int x = root[in[u]],y = root[out[u]];
    for(int i=32 ;i>=0 ;i--){
        int c = (v>>i)&1;
        int num = T[T[y].ne[c^1]].cnt - T[T[x].ne[c^1]].cnt;
        if(num>0) res |= (1<<i),c ^= 1;
        x = T[x].ne[c],y = T[y].ne[c];
    }
    return max(ans,res);
}

void solve(){
    dfs(1,0);tot = 0;
    for(int i=1 ;i<=twt ;i++) root[i] = insert(val[id[i]],root[i-1]);
    while(q--){
        int u,x;scanf("%d%d",&u,&x);
        printf("%I64d\n",query(u,x));
    }
}

int main(){
    while(~scanf("%d%d",&n,&q)){
        init();
        for(int i=1 ;i<=n ;i++) scanf("%d",&val[i]);
        for(int i=2 ;i<=n ;i++){
            int x;scanf("%d",&x);
            add(x,i);add(i,x);
        }
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值