CF2033G 题解

Descr

给你一棵 n 个点的有根树,根是 1

共有 q 次询问,每次询问形如 v,k,问从 v 开始往上走 k 步到达的点的的子树内距离 v 最远的点有多远。

n,q2×105

Sol

首先 dfs 一遍,把每个点往下走到最深的点有多远,记为 maxdepu

考虑答案的式子,假设 v 向上走到 u 然后往下走到 u 的子树里最深的点,答案为 maxu{maxdepu+depvdepu}

把贡献拆出来:maxu{maxdepudepu}+depv

但是这样做不对,有可能会出现 v 往上走到 u 再返回 v 往下走这样刷步数的情况。。。所以我们考虑把这种情况的影响消除。

一种解决方法比较聪明,记每个点的点权 wu 为其父亲往下走除去 u 这个子树的 maxdep 再减去父亲的 dep。查询直接树上倍增 k 步查最大值即可。

另一种是离线解法。。考虑把询问挂到点上,然后在树上再 dfs 一遍求出答案。建立一个线段树,dfs 进入一个点的时候求出跟上面类似的东西放到线段树下标 depu 的位置,退出的时候清空。遇到一个询问就是线段树区间 max

Code

我的是离线做法。

#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int,int> pii;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
const int N=2e5+10,T=20,INF=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
int n,q;
struct edge{
	int v,nxt;
}e[N*2];
int head[N],cnt=2;
void add(int u,int v){
	e[cnt].v=v;
	e[cnt].nxt=head[u];
	head[u]=cnt++;
}
int mxd[N],dep[N],L[N],R[N],ans[N];
vector<pii> qr[N];
void dfs(int u,int f){
	dep[u]=dep[f]+1;
	vector<int> son;
	for(int i=head[u];i;i=e[i].nxt){
		if(e[i].v!=f){
			son.pb(e[i].v);
			dfs(e[i].v,u);
			mxd[u]=max(mxd[u],mxd[e[i].v]+1);
		}
	}
	if(son.size()==1)L[son[0]]=R[son[0]]=-INF;
	else if(son.size()>=2){
		for(int i=1;i<son.size();i++)L[son[i]]=max(L[son[i-1]],mxd[son[i-1]]+1-dep[u]);
		for(int i=son.size()-2;i>=0;i--)R[son[i]]=max(R[son[i+1]],mxd[son[i+1]]+1-dep[u]);
	}
}
int t[N*4];
void update(int p,int l,int r,int pos,int val){
	if(l==r){
		t[p]=val;
		return;
	}
	int m=(l+r)>>1;
	if(pos<=m)update(p*2,l,m,pos,val);
	else update(p*2+1,m+1,r,pos,val);
	t[p]=max(t[p*2],t[p*2+1]);
}
int query(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R)return t[p];
	int m=(l+r)>>1,ans=-INF;
	if(L<=m)ans=max(ans,query(p*2,l,m,L,R));
	if(R>m)ans=max(ans,query(p*2+1,m+1,r,L,R));
	return ans;
}
void build(int p,int l,int r){
	t[p]=-INF;
	if(l==r)return;
	int m=(l+r)>>1;
	build(p*2,l,m);
	build(p*2+1,m+1,r);
}
void dfs2(int u,int fa){
	for(pii q:qr[u]){
		ans[q.se]=max({mxd[u],dep[u]+query(1,1,n,max(1ll,dep[u]-q.fi),dep[u]),dep[u]-max(1ll,dep[u]-q.fi)});
	}
	for(int i=head[u];i;i=e[i].nxt){
		if(e[i].v!=fa){
			update(1,1,n,dep[u],max(L[e[i].v],R[e[i].v]));
			dfs2(e[i].v,u);
		}
	}
	update(1,1,n,dep[u],-INF);
}
void solve(){
	cin>>n;
	fill(L,L+n+10,-INF);
	fill(R,R+n+10,-INF);
	for(int i=1;i<n;i++){
		int u,v;cin>>u>>v;
		add(u,v),add(v,u);
	}
	dfs(1,1);
	cin>>q;
	for(int i=1;i<=q;i++){
		int v,k;cin>>v>>k;
		qr[v].pb(mp(k,i));
	}
	build(1,1,n);
	dfs2(1,1);
	for(int i=1;i<=q;i++)cout<<ans[i]<<' ';
	cout<<'\n';
	for(int i=1;i<=n;i++)qr[i].clear();
	fill(head,head+n+10,0);
	cnt=2;
	fill(mxd,mxd+n+10,0);
	fill(dep,dep+n+10,0);
	fill(t,t+n*4+10,0);
	fill(ans,ans+q+10,0);
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--)solve();
	return 0;
}
posted @   Linge_Zzzz  阅读(4)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示