ABC138D solution

Problem

给定一个有 \(n\) 个节点的树,进行 \(m\) 次操作,每次操作形如 x val,把以 \(x\) 节点为根的子树的所有节点的权值 \(+val\)

link->https://atcoder.jp/contests/abc138/tasks/abc138_d

Solution

我们可以参考线段树区间修、区间查时所用的懒标记——或者说是延迟标记,它将一个改动 \(x\) 但暂时用不到的值保存在 \(x\) 节点的若干级祖先 \(y\) 上,等需要查询 \(x\) 节点的值的时候,在把保存在 \(y\) 的值传给 \(x\),以保证优秀的复杂度。

那么在本题中,我们将 \(val\) 加到以 \(x\) 节点为根的子树的所有节点上时,我们暂时并不会查询这些节点的值(而是在最终查询),那么我们可以仿照“懒标记”,暂时性的把 \(val\) 值保存在 \(x\) 节点上,最终再把保存在该节点上的值以优秀的复杂度传给它的所有子节点。

考虑到每个节点只遍历一次,所以复杂度为 \(O(n)\)

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int f[N],tag[N],n,head[N],tot,m;
struct node { //链式前向星存图
	int to,nxt;
}; node Edge[N<<1];
void Add_Edge(int u,int v) {
	++tot; Edge[tot]=node{v,head[u]}; head[u]=tot;
}
void Dfs(int u,int fa) { 
	int x=tag[u]; tag[u]=0; f[u]+=x; //将懒标记的值加在该节点的答案上(话说不区分 tag 和 f也行)
	for(int i=head[u];i;i=Edge[i].nxt) {
		int v=Edge[i].to; 
		if(v!=fa) {
			tag[v]+=x; Dfs(v,u); //传给子节点 v
		}
	}
}
signed main() {
	cin>>n>>m;
	for(int i=1;i<n;i++) {
		int u,v;
		cin>>u>>v;
		Add_Edge(u,v);
		Add_Edge(v,u);
	}
	while(m--) {
		int u,val;
		cin>>u>>val;
		tag[u]+=val; //懒标记
	}
	Dfs(1,0); //下传所有懒标记
	for(int i=1;i<=n;i++)
		cout<<f[i]<<" ";
	return 1;
}
posted @ 2022-08-02 08:59  lsj2009  阅读(35)  评论(0)    收藏  举报