Description
给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。
Input
第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v,k),表示一组询问。
Output
M行,表示每个询问的答案。最后一个询问不输出换行符
HINT
N,M<=100000
题目分析
由于树上路径不方便以序列方式维护
所以尝试差分
每棵主席树维护该节点到根的路径上每个数值出现次数
那么u到v得路径上的信息可表示为
u的主席树+v的主席树-lca(u,v)的主席树-fa[lca(u,v)]的主席树
建立主席树一次dfs即可
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=100010;
int n,m;
int a[maxn],b[maxn];
int pos[maxn],cnt;
struct node{int v,nxt;}E[maxn<<1];
int head[maxn],tot;
int fa[maxn],dep[maxn],top[maxn];
int size[maxn],son[maxn];
int lft[maxn<<5],rht[maxn<<5];
int rt[maxn<<5],sum[maxn<<5],sz;
int ans;
void add(int u,int v)
{
E[++tot].nxt=head[u];
E[tot].v=v;
head[u]=tot;
}
void dfs1(int u,int pa)
{
size[u]=1;
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v==pa) continue;
dep[v]=dep[u]+1; fa[v]=u;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;
if(son[u]) dfs2(son[u],tp);
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int update(int pre,int ll,int rr,int x)
{
int rt=++sz;
lft[rt]=lft[pre]; rht[rt]=rht[pre]; sum[rt]=sum[pre]+1;
if(ll<rr)
{
int mid=ll+rr>>1;
if(x<=mid) lft[rt]=update(lft[pre],ll,mid,x);
else rht[rt]=update(rht[pre],mid+1,rr,x);
}
return rt;
}
void dfs(int u)
{
int x=lower_bound(pos+1,pos+1+cnt,a[u])-pos;
rt[u]=update(rt[fa[u]],1,cnt,x);//以fa[u]为上一棵主席树建树
for(int i=head[u];i;i=E[i].nxt)
if(E[i].v!=fa[u]) dfs(E[i].v);
}
int LCA(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]>dep[top[v]]) u=fa[top[u]];
else v=fa[top[v]];
}
if(dep[u]<dep[v]) return u;
else return v;
}
int query(int u,int v,int lca,int gra,int ll,int rr,int k)
{
if(ll==rr) return ll;
int x=sum[lft[u]]+sum[lft[v]]-sum[lft[lca]]-sum[lft[gra]];//差分得路径
int mid=ll+rr>>1;
if(x>=k) return query(lft[u],lft[v],lft[lca],lft[gra],ll,mid,k);
else return query(rht[u],rht[v],rht[lca],rht[gra],mid+1,rr,k-x);
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i)
a[i]=b[i]=read();
sort(b+1,b+1+n);
for(int i=1;i<=n;++i)
if(i==1||b[i]!=b[i-1])
pos[++cnt]=b[i];
for(int i=1;i<n;++i)
{
int u=read(),v=read();
add(u,v); add(v,u);
}
dfs1(1,0); dfs2(1,1);//树剖
dfs(1);//深搜建立主席树
while(m--)
{
int u=read()^ans,v=read(),k=read();
int lca=LCA(u,v);
ans=pos[query(rt[u],rt[v],rt[lca],rt[fa[lca]],1,cnt,k)];//差分得到u到v的路径
printf("%d\n",ans);
}
return 0;
}