题目大概意思
给出一棵树,树上的每个结点的值。
询问从结点a到b的路径上的值,对于值val来说,最大的异或和是多少
这是一道经典可持久化字典树的题目
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=30;
int ch[N*35][2],tot,num[N*32],a[N],T[N];
int fa[N][21],dep[N],n,m;
vector<int>g[N];
void add(int now,int last,int val)//在结点now继承父亲的属性构造可持久化字典树
{
for(int i=M;i>=0;i--)//分解结点val的值
{
int id=(val>>i)&1;
ch[now][id]=++tot;
now=ch[now][id],last=ch[last][id];//父亲结点和该结点的字典树结点同时向下,用于构造该结点的字典树的时候
memcpy(ch[now],ch[last],sizeof(ch[last]));//继承父亲的值
num[now]=num[last]+1;//基础上加一,因为多了该节点
}
}
void dfs(int u,int father,int d)
{
fa[u][0]=father,dep[u]=d;//lca的预处理
for(int i=1;i<=20;i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
T[u]=++tot;//tot指的是字典树的结点,T[u]表示,u结点字典树的根,即入口
memset(ch[tot],0,sizeof(ch[tot]));//初始化字典树
num[tot]=0;
add(T[u],T[father],a[u]);//构造持久化字典树
for(int v:g[u])
{
if(v==father) continue;
dfs(v,u,d+1);
}
}
int work(int u,int v,int lca,int lcaf,int x)
{
int res=0;
for(int i=M;i>=0;i--)//关键,对于询问值来说,要想得到最大值,对于每一位来说,位操作都是独立的,所以可以自高向低位贪心每一位最大
{
int id=!((x>>i)&1);//期望贪心字典树的位
if(num[ch[u][id]]+num[ch[v][id]]-num[ch[lca][id]]-num[ch[lcaf][id]])//关键一步,lcaf指的是lca的父亲,至于为什么这样操作,画图便知
res|=(1<<i);//如果有那一位可以进行异或,便下去
else id^=1;//没有就取不到了
u=ch[u][id],v=ch[v][id],lca=ch[lca][id],lcaf=ch[lcaf][id];//四个结点的字典树结点,同时下去。
}
return res;
}
int lca(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
for(int i=20;i>=0;i--)
if(dep[fa[u][i]]>=dep[v])
u=fa[u][i];
if(u==v) return u;
for(int i=20;i>=0;i--)
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
void solve()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),g[i].clear();
for(int i=2;i<=n;i++)
{
int u=i,v;
scanf("%d",&v);
g[u].push_back(v);
g[v].push_back(u);
}
memset(ch[0],0,sizeof(ch[0]));
tot=num[0]=0;
dfs(1,0,1);
for(int i=1;i<=m;i++)
{
int u,v,x;
scanf("%d %d %d",&x,&u,&v);
int LCA=lca(u,v);
int ans=work(T[u],T[v],T[LCA],T[fa[LCA][0]],x);
printf("%d\n",ans);
}
}
int main()
{
//freopen("C.in","r",stdin);
int T;
scanf("%d",&T);
while(T--)
solve();
return 0;
}
对于每个结点来说,信息都是继承自父亲结点的信息,那么从根结点下去,就相当于是一个前缀和的形式,所以,可以通过减法,来获取树上某个区间的信息。可持久化字典树需要的空间,相当于每个结点都建立了一颗01字典树。可以开辟。然后,通过求取lca的方式,将两端链拼接在一起,就可以拿到我们想要的信息了,这就是可持久化的精髓所在