CF893F
首先,我们发现,这个题只需要子树内的答案,且只需要维护最小值。
注意到对于两个点 \(i,j\),若 \(dep_i>dep_j\),且 \(val_i\ge val_j\),则对于 \(lca(i,j)\) 及其它的父亲,\(i\) 都是一个无用的点。
注意到 \(n\le 10^5,m\le 10^6\),这启发我们进行预处理以做到 \(O(\log n)\) 单次询问。
考虑进行广搜,设当前在搜 \(dep=k\) 的点,动态维护 \(u\) 的子树中距离与它不超过 \(k-dep_u\) 的节点的最小值 \(w_u\)。
我们暴力地从当前点 \(u\) 往上跳父亲,若遇到第一个 \(x\) 满足 \(w_x\le val_u\),则对于 \(x\) 以及其祖先,\(u\) 都是一个无用的点,直接 break
。
在没有遇到之前,我们边跳边用一颗动态开点线段树维护答案,也即第 \(x\) 棵线段树的第 \(i\) 个位置是有效的距离为 \(i\) 的点的权值。这里满足这个权值单调递减。然后令 \(w'_x=val_u\)
然后我们暴力查区间最小值即可。
void init(){
queue<int>q;
q.push(r);
while(!q.empty()){
int u=q.front();q.pop();
dep[u]=dep[fa[u]]+1;
int dis=1,v=fa[u];
insert(rt[u],1,n,dis,val[u]);
while(val[v]>val[u]){
++dis;insert(rt[v],1,n,dis,val[u]);val[v]=val[u];
v=fa[v];
}
for(auto v:g[u]){
if(v==fa[u])continue;
q.push(v);
}
}
}
int ask(int u,int k){
return find(rt[u],1,n,1,k);
}
signed main(){
ios::sync_with_stdio(false);
cin>>n>>r;val[0]=-0x3f3f3f3f;
for(int i=1;i<=n;i++)cin>>val[i];
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
g[u].push_back(v);g[v].push_back(u);
}
for(int i=1;i<=n;i++){
random_shuffle(g[i].begin(),g[i].end());
}
dfs(r,0);
init();
cin>>m;int lst=0;
while(m--){
int u,k;cin>>u>>k;
u=(u+lst)%n+1;k=(k+lst)%n;++k;
lst=ask(u,k);cout<<lst<<"\n";
}
}
标签:CF893F,val,fa,int,dep,lst,dis
From: https://www.cnblogs.com/oierpyt/p/17685335.html