Count on a tree
題目描述
給定一棵 n 個節點的樹,每個點有一個權值。有 m 個詢問,每次給你 u,v,k,你需要回答 u xor last 和 v 這兩個節點間第 k 小的點權。
其中 last 是上一個詢問的答案,定義其初始爲 0,即第一個詢問的 u 是明文。
輸入格式
第一行兩個整數 n,m。
第二行有 n 個整數,其中第 i 個整數表示點 i 的權值。
後面 n−1 行每行兩個整數 x,y,表示點 x 到點 y 有一條邊。
最後 m 行每行兩個整數 u,v,k 表示一組詢問。
輸出格式
m 行,每行一個正整數表示每個詢問的答案。
主席樹十分明顯,但是這是樹上問題,不是線性區間;
其實是一樣的,線性區間的主席樹就是前綴和概念,樹也可以這樣運用,就是樹上前綴和;
每個點是它的父親連接過來的,所以必須遞歸建主席樹;
那麼u,v區間的第k大不就是tr[u]+tr[v]-tr[lca(u,v)]-tr[fa(lca(u,v))] ;就是前綴和概念;
代碼:
#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=100100;
const int M=50100;
const LL mod=1e9+7;
inline int read(){//非常重要的快速讀入代碼
int x=0,sign=1;
char c=getchar();
while(c>'9'||c<'0'){//判斷符號
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9'){//轉換數
x=x*10+c-'0';
c=getchar();
}
return x*sign;
}
int n,m,ss,head[N],cnt,a[N],b[N],ans[N];
int size[N],fa[N],son[N],deep[N];
int top[N];
struct Node{
int to,nex;
}edge[N*2];
void add(int p,int q){edge[cnt].to=q,edge[cnt].nex=head[p],head[p]=cnt++;}
void dfs1(int p,int fat){
size[p]=1,fa[p]=fat;
int mx=-1;
for(int i=head[p];~i;i=edge[i].nex){
int q=edge[i].to;
if(q!=fat){
deep[q]=deep[p]+1;
dfs1(q,p);
size[p]+=size[q];
if(size[q]>mx) son[p]=q,mx=size[q];
}
}
}
void dfs2(int p,int topp){
top[p]=topp;
if(!son[p]) return;//沒有重兒子,也就是葉子結點
dfs2(son[p],topp);//先處理重兒子
for(int i=head[p];~i;i=edge[i].nex){
int q=edge[i].to;
if(q==fa[p]||q==son[p]) continue;
dfs2(q,q);
}
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);//保證x結點的頂點更深
x=fa[top[x]];
}
if(deep[x]<deep[y]) swap(x,y);//保證x結點的頂點更深
return y;
}
////
int tr[N],L[N*40],R[N*40],sum[N*40],root;
int build(int l,int r){
int rt=++root;
int d=(l+r)>>1;
if(l<r){
L[rt]=build(l,d);
R[rt]=build(d+1,r);
}
return rt;
}
int update(int pre,int l,int r,int pos){
int rt=++root;
L[rt]=L[pre],R[rt]=R[pre],sum[rt]=sum[pre]+1;
int d=(l+r)>>1;
if(l<r){
if(pos<=d) L[rt]=update(L[pre],l,d,pos);
else R[rt]=update(R[pre],d+1,r,pos);
}
return rt;
}
int query(int r1,int r2,int r3,int r4,int l,int r,int k){
if(l==r) return l;
int s=sum[L[r1]]+sum[L[r2]]-sum[L[r3]]-sum[L[r4]];
int d=(l+r)>>1;
if(s>=k) return query(L[r1],L[r2],L[r3],L[r4],l,d,k);
else return query(R[r1],R[r2],R[r3],R[r4],d+1,r,k-s);
}
void dfs(int p,int ft){
tr[p]=update(tr[ft],1,ss,a[p]);
for(int i=head[p];~i;i=edge[i].nex){
int q=edge[i].to;
if(q!=ft){
dfs(q,p);
}
}
}
////
int main(){
memset(head,-1,sizeof(head));
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read(),b[i]=a[i];
for(int i=1;i<n;i++){
int u,v;u=read(),v=read();
add(u,v),add(v,u);
}
///離散化
sort(b+1,b+n+1);
ss=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++){
int t=lower_bound(b+1,b+ss+1,a[i])-b;
ans[t]=a[i];
a[i]=t;
}
///
dfs1(1,0),dfs2(1,1);
tr[0]=build(1,ss);
dfs(1,0);//遞歸的建主席樹
// for(int i=1;i<=n;i++) tr[i]=update(tr[fa[i]],1,ss,a[i]);//建主席樹
int last=0;
while(m--){
int u,v,k;u=read(),v=read(),k=read();
u^=last;
last=ans[query(tr[u],tr[v],tr[lca(u,v)],tr[fa[lca(u,v)]],1,ss,k)];
printf("%d\n",last);
}
return 0;
}