题目链接:Codeforces - Subtree Minimum Query
容易想到,我们按照深度插入权值,用主席树合并答案。
但是主席树无法维护最值,故使用线段树合并即可。
因为要存每个子树的信息,所以合并的时候需要新开节点。
每次查询的时候,只查询深度dep[x]到dep[x]+k即可。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e5+10;
int n,m,a[N],r,dep[N],res;
int rt[N],mi[N*40],lc[N*40],rc[N*40],cnt;
vector<int> g[N];
inline void add(int a,int b){g[a].push_back(b),g[b].push_back(a);}
void change(int &p,int l,int r,int x,int val){
if(!p) p=++cnt;
if(l==r){mi[p]=val; return ;}
int mid=l+r>>1;
if(x<=mid) change(lc[p],l,mid,x,val);
else change(rc[p],mid+1,r,x,val);
mi[p]=min(mi[lc[p]],mi[rc[p]]);
}
int ask(int p,int l,int r,int ql,int qr){
if(!p) return 0x3f3f3f3f;
if(l==ql&&r==qr) return mi[p];
int mid=l+r>>1;
if(qr<=mid) return ask(lc[p],l,mid,ql,qr);
else if(ql>mid) return ask(rc[p],mid+1,r,ql,qr);
else return min(ask(lc[p],l,mid,ql,mid),ask(rc[p],mid+1,r,mid+1,qr));
}
int merge(int x,int y,int l,int r){
if(!x||!y) return x+y;
int mid=l+r>>1;
if(l==r){mi[++cnt]=min(mi[x],mi[y]); return cnt;}
int p=++cnt;
lc[p]=merge(lc[x],lc[y],l,mid);
rc[p]=merge(rc[x],rc[y],mid+1,r);
mi[p]=min(mi[lc[p]],mi[rc[p]]);
return p;
}
void dfs(int x,int fa){
dep[x]=dep[fa]+1; change(rt[x],1,n,dep[x],a[x]);
for(auto to:g[x]) if(to!=fa){
dfs(to,x); rt[x]=merge(rt[x],rt[to],1,n);
}
}
signed main(){
cin>>n>>r; memset(mi,0x3f,sizeof mi);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1,a,b;i<n;i++) scanf("%d %d",&a,&b),add(a,b);
dfs(r,r); cin>>m;
while(m--){
int x,k; scanf("%d %d",&x,&k);
x=(x+res)%n+1,k=(k+res)%n;
printf("%d\n",res=ask(rt[x],1,n,dep[x],min(dep[x]+k,n)));
}
return 0;
}