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