题意:
有一棵n个节点的树,每个节点有一个权值,询问u节点到v节点的链上异或x的最大值
思路:
dfs建树,对于u这个版本,它的上一个版本是它的父亲节点。然后就是查询了,这类树上询问一条链的问题,很多都是利用lca解决的。求出u,v两个点的lca,u到lca其实应该query(u,fa[lca])才行,v和lca同样。但是也可以query(u/v,lca),最后在把lca的位置单独拿出来考虑一下就可以了。
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
int a[N];
int head[N],cnt;
struct _edge{
int v,nxt;
}edge[N<<1];
void add_edge(int u,int v){
edge[++cnt].v=v;
edge[cnt].nxt=head[u];
head[u]=cnt;
}
int ch[N<<5][2];
int sz[N<<5];
int root[N],tot;
void update(int last,int cur,int v){
for(int i=15;i>=0;i--){
ch[cur][0]=ch[last][0];
ch[cur][1]=ch[last][1];
sz[cur]=sz[last]+1;
int j=1&(v>>i);
ch[cur][j]=++tot;
cur=ch[cur][j];
last=ch[last][j];
}
sz[cur]=sz[last]+1;
}
int dep[N];
int f[N][20];
void dfs(int u,int fa){
dep[u]=dep[fa]+1;
f[u][0]=fa;
for(int i=1;i<18;i++){
f[u][i]=f[f[u][i-1]][i-1];
}
update(root[fa],root[u]=++tot,a[u]);
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].v;
if(v==fa)continue;
dfs(v,u);
}
}
int lca(int a,int b)
{
if(dep[a]>dep[b])swap(a,b);
if(dep[a]<dep[b])
{
int del=dep[b]-dep[a];
for(int i=0;i<18;i++)
if(del&(1<<i))b=f[b][i];
}
if(a==b)return a;
for(int i=17;i>=0;i--)
{
if(f[a][i]!=f[b][i])
{
a=f[a][i],b=f[b][i];
}
}
return f[a][0];
}
int query(int x,int y,int v)
{
int z=lca(x,y);int res=a[z]^v;
x=root[x],y=root[y],z=root[z];
int ret=0;
for(int i=15;i>=0;i--){
int c=1&(v>>i);
if(sz[ch[x][!c]]+sz[ch[y][!c]]-2*sz[ch[z][!c]]>0){//判断之间有没有合法的数字
ret+=1<<i;
c=!c;
}
x=ch[x][c];
y=ch[y][c];
z=ch[z][c];
}
return max(ret,res);
}
int main(){
while (~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
memset(head,-1, sizeof(head));cnt=0;
for(int i=1,u,v;i<n;i++){
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
tot=0;
dfs(1,0);
for(int i=1,x,y,z;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
printf("%d\n",query(x,y,z));
}
}
return 0;
}