hdoj6191

题目大意:一棵树,n个结点,每个结点都有一个值,m次询问,问以u为根的子树结点中与x异或的最大值。

 

        先跑一边dfs序,子树问题就转化成了区间问题,这样最大异或值就可通过字典树来求了,但是与我们做过的最大异或值那道题的区别在于询问有区间限制,即每次只能在[L,R]的字典树上跑。。。这时候就要用可持久化字典树了。

        可持久化字典树建树的思想和主席树一样,建n棵树字典树,第i棵树的维护的是区间[1,i]的数据,这样区间[L,R]的字典树就可以通过 第 R 和 L-1 这两棵树维护的信息之差(相减)来得到了 。

 

       弄懂可持久化字典树的原理后,这道题就变成模板题了。

 

   代码

 


#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
vector<int>g[100010];
int A[100010],B[100010],C[100010],D[40],siz[100010],sum;
int T[4000000][2],v[4000000],cnt,root[100010];
void dfs(int u,int f){
     int i,v;
     siz[u]=1;
     for(i=0;i<g[u].size();i++){
         v=g[u][i];
         if(v!=f){
             dfs(v,u);
             siz[u]+=siz[v];
         }
     }
     B[++sum]=A[u];
     C[u]=sum;
}
int q(int l,int r){
     int i,u,ans=0;
     for(i=32;i>=1;i--){
         u=D[i];
         ans*=2;
         if(v[T[r][!u]]-v[T[l][!u]]){
             ans++;
             r=T[r][!u];
             l=T[l][!u];
         }
         else{
             r=T[r][u];
             l=T[l][u];
         }
     }
     return ans;
}
void insert(int &f,int x){
     if(x==-1) return;
     T[++cnt][0]=T[f][0];
     T[cnt][1]=T[f][1];
     v[cnt]=v[f]+1;
     f=cnt;
     insert(T[f][D[x]],x-1);
}
int main(){
    int n,m,i,j,a,b;
    while(scanf("%d%d",&n,&m)!=EOF){
        sum=0;
        for(i=0;i<=n;i++) g[i].clear();
        for(i=1;i<=n;i++) scanf("%d",&A[i]);
        for(i=2;i<=n;i++){
            scanf("%d",&a);
            g[a].push_back(i);
        }
        dfs(1,0);
        cnt=0;
        memset(T,0,sizeof(T));
        memset(v,0,sizeof(v));
        for(i=1;i<=n;i++){
             a=B[i];
             for(j=1;j<=32;j++){
                 D[j]=a%2;
                 a/=2;
             }
             root[i]=root[i-1];
             insert(root[i],32);
        }
        while(m--){
            scanf("%d%d",&a,&b);
            for(i=1;i<=32;i++){
                D[i]=b%2;
                b/=2;
            }
            int r=C[a],l=C[a]-siz[a];
            int ans=q(root[l],root[r]);
            printf("%d\n",ans);
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值