题目大意:一棵树,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;
}