CF893F

发布时间 2023-09-07 16:34:57作者: weakpyt

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";
	}
}

code