水题!
第一眼以为是根号分治。
由于题目的要求是全部 m m m 次命令执行完之后才询问,这意味着可以像差分一样最后统一跑一遍求出答案。
命令 2,我们可以打标记处理, d d a x dda_x ddax 表示所有与点 x x x 相邻的点需要增加的数量,最后全部加上就行了。
cpp
for(int x=1;x<=n;x++){
for(int i=0;i<a[x].size();i++){
int y=a[x][i];
ans[x]+=dda[y];
}
}
因为是树,只有 n − 1 n-1 n−1 条边,时间复杂度 O ( N ) O(N) O(N)。
对于命令 1,将点 x x x 的子树全部加上一个数,修改一堆,查询一个,容易想到差分。
还是标记, a d d x add_x addx 表示点 x x x 及其子树需要加上的数,最后从根节点往下 dfs 一遍即可求出。
cpp
void dfs(int x,int fa,int sum){
sum+=add[x];//先更新 sum,当前的点也是子树的一部分
ans[x]+=sum;
for(int i=0;i<a[x].size();i++){
int y=a[x][i];
if(y==fa)continue;
dfs(y,x,sum);
}
}
code
cpp
#include<bits/stdc++.h>
#define int long long
#define endl putchar('\n')
using namespace std;
const int N=1e6+5;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
void putstr(string s){
for(int i=0;i<s.size();i++)putchar(s[i]);
}
int n,m;
int ans[N];
int d[N];
vector<int>a[N];
int add[N];
int dda[N];
void dfs(int x,int fa,int sum){
sum+=add[x];
ans[x]+=sum;
for(int i=0;i<a[x].size();i++){
int y=a[x][i];
if(y==fa)continue;
dfs(y,x,sum);
}
}
signed main(){
n=read();
for(int i=1;i<=n;i++)ans[i]=read();
for(int i=1;i<n;i++){
int u=read(),v=read();
a[u].push_back(v);
a[v].push_back(u);
}
m=read();
while(m--){
int op=read(),x=read(),y=read();
if(op==2){
ans[x]+=y,dda[x]+=y;
}
else{
add[x]+=y;
}
}
for(int x=1;x<=n;x++){
for(int i=0;i<a[x].size();i++){
int y=a[x][i];
ans[x]+=dda[y];
}
}
dfs(1,1,0);
int q=read();
while(q--){
int x=read();
print(ans[x]),endl;
}
}