hdu6191 Query on A Tree(可持久化字典树)

题意:

给定n个顶点的树,树根为1,
每个点有点权a(i),
q次询问,每次询问给出u,x
要求在以u为根的子树中,找到一个点,
满足这个点与x的异或值最大,输出这个最大异或值。

数据范围:n,q<=1e5

解法:

子树问题可以用dfs序转化为区间问题,
那么每个询问就变为在区间[l,r]上的查询.
在序列上建可持久化01字典树就行了.

code:

#include
using namespace std;
const int maxm=1e5+5;
vector<int>g[maxm];
int ans[maxm];
int a[maxm];
int n,q;
//
int nt[maxm*60][2];
int cnt[maxm*60];
int rt[maxm],tot;
void add(int x,int last,int k){
    for(int i=30;i>=0;i--){
        int v=(x>>i&1);
        //copy
        nt[k][0]=nt[last][0];
        nt[k][1]=nt[last][1];
        //
        tot++;
        nt[tot][0]=nt[tot][1]=0;
        nt[k][v]=tot;
        //
        k=nt[k][v];
        last=nt[last][v];
        cnt[k]=cnt[last]+1;
    }
}
int ask(int x,int l,int r){
    int ans=0;
    for(int i=30;i>=0;i--){
        int v=(x>>i&1);
        if(cnt[nt[r][v^1]]-cnt[nt[l][v^1]]){
            r=nt[r][v^1];
            l=nt[l][v^1];
            ans+=(1<<i);
        }else{
            r=nt[r][v];
            l=nt[l][v];
        }
    }
    return ans;
}
//
int L[maxm],R[maxm],idx;
int rk[maxm];
void dfs(int x){
    L[x]=++idx;
    rk[idx]=x;
    for(int v:g[x]){
        dfs(v);
    }
    R[x]=idx;
}
//
void init(){
    tot=0;
    nt[tot][0]=nt[tot][1]=0;
    rt[tot]=0;
    idx=0;
}
signed main(){
    while(scanf("%d%d",&n,&q)!=EOF){
        init();
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)g[i].clear();
        for(int i=2;i<=n;i++){
            int fa;scanf("%d",&fa);
            g[fa].push_back(i);
        }
        dfs(1);
        for(int i=1;i<=n;i++){
            //
            tot++;
            nt[tot][0]=nt[tot][1]=0;
            //
            rt[i]=tot;
            add(a[rk[i]],rt[i-1],rt[i]);
        }
        while(q--){
            int u,x;scanf("%d%d",&u,&x);
            int ans=ask(x,rt[L[u]-1],rt[R[u]]);
            printf("%d\n",ans);
        }
    }
    return 0;
}

你可能感兴趣的:(hdu6191 Query on A Tree(可持久化字典树))