题意:有一棵树,树根为1,树上的每个结点都有一个数字x。给出Q组询问,每组询问有两个值u,x,代表询问以结点u为根的子树中的某一个数与x的最大异或值。
解法一:dfs序+可持久化字典树。看到子树询问,首先要想到dfs序啦。可以对所有结点按dfs序依次建立可持久化的字典树,字典树上的每个结点除了要保存它的后继结点以外,还要保存这个结点出现的次数num。询问结点u时,对[bg[u],ed[u]]上的字典树的num做差,字典树上剩下的num>0的结点即为可行状态,然后按普通的字典树的查询方法查询就是了。
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; const int M=30; int n,Q,head[N],nxt[N],to[N],val[N],nEdge; int rt[N],num[N*32],go[N*32][2],nnode; int bg[N],ed[N],tot; void AddEdge(int u,int v) { nxt[nEdge]=head[u],to[nEdge]=v,head[u]=nEdge++; } int update(int v,int x,int bit) { int u=++nnode; num[u]=num[v]+1; if(bit<0)return u; int t=(x>>bit)&1; go[u][t]=update(go[v][t],x,bit-1); go[u][t^1]=go[v][t^1]; return u; } void dfs(int u) { bg[u]=++tot; rt[tot]=update(rt[tot-1],val[u],M); for(int e=head[u]; ~e; e=nxt[e]) { int v=to[e]; dfs(v); } ed[u]=tot; } int query(int u,int v,int x,int bit,int now) { if(bit<0)return now; int t=(x>>bit)&1; if(go[u][t^1]-go[v][t^1]>0) return query(go[u][t^1],go[v][t^1],x,bit-1,now|(1<<bit)); else return query(go[u][t],go[v][t],x,bit-1,now); } int main() { while(scanf("%d%d",&n,&Q)==2) { memset(head,-1,sizeof head); nEdge=nnode=tot=0; for(int i=1; i<=n; ++i)scanf("%d",&val[i]); for(int i=2; i<=n; ++i) { int u; scanf("%d",&u); AddEdge(u,i); } rt[0]=go[0][0]=go[0][1]=num[0]=0; dfs(1); while(Q--) { int u,x; scanf("%d%d",&u,&x); printf("%d ",query(rt[ed[u]],rt[bg[u]-1],x,M,0)); } } return 0; }
解法二:离线+字典树合并。可以自底而上来回答询问,每回答完一个结点下的所有子节点的询问,就将这个结点的字典树与它所有子节点的字典树合并。
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; const int M=30; int n,Q,head[N],nxt[N],to[N],val[N],nEdge; int rt[N],go[N*32][2],nnode,ans[N]; struct QUERY { int x,i; }; vector<QUERY> qr[N]; void AddEdge(int u,int v) { nxt[nEdge]=head[u],to[nEdge]=v,head[u]=nEdge++; } int build(int x,int bit) { int u=++nnode; if(bit<0)return u; int t=(x>>bit)&1; go[u][t]=build(x,bit-1); go[u][t^1]=0; return u; } int Merge(int u,int v,int bit) { if(!u)return v; if(!v)return u; if(bit<0)return u; go[u][0]=Merge(go[u][0],go[v][0],bit-1); go[u][1]=Merge(go[u][1],go[v][1],bit-1); return u; } int query(int u,int x,int bit,int now) { if(bit<0)return now; int t=(x>>bit)&1; if(go[u][t^1]) return query(go[u][t^1],x,bit-1,now|(1<<bit)); else return query(go[u][t],x,bit-1,now); } void dfs(int u) { for(int e=head[u]; ~e; e=nxt[e]) { int v=to[e]; dfs(v); } for(int e=head[u]; ~e; e=nxt[e]) { int v=to[e]; rt[u]=Merge(rt[u],rt[v],M); } for(int i=0; i<qr[u].size(); ++i) { ans[qr[u][i].i]=query(rt[u],qr[u][i].x,M,0); } } int main() { while(scanf("%d%d",&n,&Q)==2) { memset(head,-1,sizeof head); nEdge=nnode=0; for(int i=0; i<=n; ++i)qr[i].clear(); for(int i=1; i<=n; ++i)scanf("%d",&val[i]); for(int i=2; i<=n; ++i) { int u; scanf("%d",&u); AddEdge(u,i); } for(int i=1; i<=n; ++i)rt[i]=build(val[i],M); for(int i=0; i<Q; ++i) { int u,x; scanf("%d%d",&u,&x); qr[u].push_back({x,i}); } dfs(1); for(int i=0; i<Q; ++i)printf("%d ",ans[i]); } return 0; }
还有一种解法是“可持久化字典树合并”,即父结点继承所有子结点的字典树,继承的方式与字典树合并一样,只不过把两棵树的公共部分开新结点就好,也是在线的,但空间消耗较大。
#define FRER() freopen("i.txt","r",stdin) #define FREW() freopen("o.txt","w",stdout) #include<bits/stdc++.h> using namespace std; const int N=1e5+10; const int M=30; int n,Q,head[N],nxt[N],to[N],val[N],nEdge; int rt[N],go[N*64][2],nnode,ans[N]; void AddEdge(int u,int v) { nxt[nEdge]=head[u],to[nEdge]=v,head[u]=nEdge++; } int build(int x,int bit) { int u=++nnode; if(bit<0)return u; int t=(x>>bit)&1; go[u][t]=build(x,bit-1); go[u][t^1]=0; return u; } int Merge(int u,int v,int bit) { if(!u)return v; if(!v)return u; if(bit<0)return u; int w=++nnode; go[w][0]=Merge(go[u][0],go[v][0],bit-1); go[w][1]=Merge(go[u][1],go[v][1],bit-1); return w; } int query(int u,int x,int bit,int now) { if(bit<0)return now; int t=(x>>bit)&1; if(go[u][t^1]) return query(go[u][t^1],x,bit-1,now|(1<<bit)); else return query(go[u][t],x,bit-1,now); } void dfs(int u) { for(int e=head[u]; ~e; e=nxt[e]) { int v=to[e]; dfs(v); } for(int e=head[u]; ~e; e=nxt[e]) { int v=to[e]; rt[u]=Merge(rt[u],rt[v],M); } } int main() { while(scanf("%d%d",&n,&Q)==2) { memset(head,-1,sizeof head); nEdge=nnode=0; for(int i=1; i<=n; ++i)scanf("%d",&val[i]); for(int i=2; i<=n; ++i) { int u; scanf("%d",&u); AddEdge(u,i); } for(int i=1; i<=n; ++i)rt[i]=build(val[i],M); dfs(1); for(int i=0; i<Q; ++i) { int u,x; scanf("%d%d",&u,&x); printf("%d ",query(rt[u],x,M,0)); } } return 0; }