/*
树上第K大.
对于每一个节点以key(权值)为下标
用一颗权值线段树维护它到根的路径的区间K大值.
然后用主席树维护.
树剖求lca.
*/
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+5,M=N*20;
struct edge{int v,next;}e[N<<1];int tot,head[N];
int n,m,sz,a[N],s[N],son[N],top[N],siz[N],fa[N],dep[N];
int root[N],sum[M],ls[M],rs[M];
int ans;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void add(int x,int y){
e[++tot].v=y;e[tot].next=head[x];head[x]=tot;
e[++tot].v=x;e[tot].next=head[y];head[y]=tot;
}
//=========================================预处理
void insert(int &k,int last,int l,int r,int p){
k=++sz;
sum[k]=sum[last]+1;
if(l==r) return ;
ls[k]=ls[last];
rs[k]=rs[last];
int mid=l+r>>1;
if(p<=mid) insert(ls[k],ls[last],l,mid,p);
else insert(rs[k],rs[last],mid+1,r,p);
}
int query(int l,int r,int x1,int x2,int x3,int x4,int K){
if(l==r) return l;
int mid=l+r>>1;
int cnt=sum[ls[x1]]+sum[ls[x2]]-sum[ls[x3]]-sum[ls[x4]];
if(K<=cnt) return query(l,mid,ls[x1],ls[x2],ls[x3],ls[x4],K);
else return query(mid+1,r,rs[x1],rs[x2],rs[x3],rs[x4],K-cnt);
}
//=========================================主席树
void dfs(int x,int f,int de){
fa[x]=f;dep[x]=de;siz[x]=1;
int p=lower_bound(s+1,s+n+1,a[x])-s;
insert(root[x],root[f],1,n,p);
for(int i=head[x];i;i=e[i].next){
if(e[i].v!=f){
dfs(e[i].v,x,de+1);
siz[x]+=siz[e[i].v];
if(siz[son[x]]<siz[e[i].v]) son[x]=e[i].v;
}
}
}
void getpos(int x,int tp){
top[x]=tp;//pos[x]=++dfs_cnt;
if(!son[x]) return ;
getpos(son[x],tp);
for(int i=head[x];i;i=e[i].next){
if(e[i].v!=fa[x]&&e[i].v!=son[x]){
getpos(e[i].v,e[i].v);
}
}
}
inline int lca(int x,int y){
fa[1]=1;//求lca,fa[1]必须=1,否则就GG了
for(;top[x]!=top[y];x=fa[top[x]]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
}
fa[1]=0;//当anc=1时,防止query(...root[anc],root[fa[anc]])出问题
return dep[x]<dep[y]?x:y;
}
//=========================================树链剖分
void Q_ans(int x,int y,int K){
int anc=lca(x,y);//类比区间[l,r]
ans=query(1,n,root[x],root[y],root[anc],root[fa[anc]],K);
printf("%d",ans=s[ans]);
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++) s[i]=a[i]=read();
for(int i=1,x,y;i<n;i++) x=read(),y=read(),add(x,y);
sort(s+1,s+n+1);
dfs(1,0,1);getpos(1,1);
for(int i=1,x,y,z;i<=m;i++){
x=read()^ans;y=read();z=read();
Q_ans(x,y,z);
if(i!=m) printf("\n");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术