CF1110F Nearest Leaf 题解

Solution

这个题的性质保证了对于任意的结点,它的子树的结点编号是连续的区间。

考虑离线,将整棵树 DFS 一遍,在这个过程中动态维护当前结点 \(u\) 到所有叶子的距离。

于是问题来了:当我们从 \(u\) DFS 到他的儿子 \(v\) 时,到叶子的距离有什么变化呢?

答案是:以 \(v\) 为根的子树距离 \(-w\),其它结点距离 \(+w\)。由于这两个都是连续区间,所以可以使用线段树维护。

具体细节见代码。

Code

语言标准:C++17。

#include<cstdio>
#include<algorithm>
#include<vector>
#include<tuple>

typedef long long ll;

const int N=5e5;
const ll inf=0x3f3f3f3f3f3f3f3f;

typedef long long ll;

struct Node{
	ll v,atag;
};

int n,m;
std::vector<std::pair<int,int>> G[N+10];
ll dis[N+10],a[N+10],ans[N+10];
int cnt,L[N+10],R[N+10],siz[N+10];
std::vector<std::tuple<int,int,int>> q[N+10];
Node t[N*4+10];

// L[u], R[u] 表示以 u 为根的子树中的结点编号的 min 和 max。
void DFS1(int u){
	L[u]=++cnt;
	siz[u]=1;
	for(auto [v,w]:G[u]){
		dis[v]=dis[u]+w;
		DFS1(v);
		siz[u]+=siz[v];
	}
	R[u]=L[u]+siz[u]-1;
}

// 区间加,区间最小值
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)

inline void pushUp(int i){
	t[i].v=std::min(t[ls(i)].v,t[rs(i)].v);
}

inline void pushA(int i,ll atag){
	t[i].v+=atag;t[i].atag+=atag;
}

inline void pushDown(int i){
	if(t[i].atag){
		pushA(ls(i),t[i].atag);
		pushA(rs(i),t[i].atag);
		t[i].atag=0;
	}
}

void build(int i,int l,int r){
	if(l==r)return t[i].v=a[l],void();
	int mid=(l+r)>>1;
	build(ls(i),l,mid);
	build(rs(i),mid+1,r);
	pushUp(i);
}

void modify(int i,int l,int r,int ql,int qr,int x){
	if(ql<=l&&r<=qr)return pushA(i,x),void();
	int mid=(l+r)>>1;
	pushDown(i);
	if(ql<=mid)modify(ls(i),l,mid,ql,qr,x);
	if(qr>mid) modify(rs(i),mid+1,r,ql,qr,x);
	pushUp(i);
}

ll query(int i,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr)return t[i].v;
	int mid=(l+r)>>1;
	pushDown(i);
	ll res=inf;
	if(ql<=mid)res=std::min(res,query(ls(i),l,mid,ql,qr));
	if(qr>mid) res=std::min(res,query(rs(i),mid+1,r,ql,qr));
	return res;
}

#undef ls
#undef rs

void DFS2(int u){
	// 处理所有在 u 结点上的询问
	for(auto [l,r,id]:q[u])
		ans[id]=query(1,1,n,l,r);
	for(auto [v,w]:G[u]){
		modify(1,1,n,1,n,w);
		modify(1,1,n,L[v],R[v],-2*w);
		DFS2(v);
		modify(1,1,n,1,n,-w);
		modify(1,1,n,L[v],R[v],2*w);
	}
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=2;i<=n;i++){
		int x,y;scanf("%d%d",&x,&y);
		G[x].push_back({i,y});
	}
	for(auto &x:G)std::sort(x.begin(),x.end());
	for(int i=1;i<=m;i++){
		int v,l,r;scanf("%d%d%d",&v,&l,&r);
		q[v].push_back({l,r,i});
	}
	DFS1(1);
	for(int i=1;i<=n;i++){
		if(siz[i]==1)a[i]=dis[i];
		else a[i]=inf; // 如果不是叶子结点,直接设为 inf,以忽略它们对答案的影响
	}
	build(1,1,n);
	DFS2(1);
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2021-11-07 19:18  registerGen  阅读(85)  评论(0)    收藏  举报