树状数组维护子树和
Problem
已知有 n 个节点,有 n−1 条边,形成一个树的结构。
给定一个根节点 k,每个节点都有一个权值,节点i的权值为 vi
给 m 个操作,操作有两种类型:
1\space a\space x :表示将节点 a 的权值加上 x
2\space a :表示求 a 节点的子树上所有节点的和(包括 a 节点本身)
Solution
有这样一个结论:某节点和其所有子树结点的时间戳dfn是连续的
我们用一个dfn时间戳,在进入时为 time1,遍历完所有子树结点之后为 time2
那么该节点与其所有子树结点的范围为一个连续的time1-time2,这个时候再用树状数组维护即可
即通过dfn时间戳(dfs序),将树形转化为连续线形段
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
ll val[maxn];
ll valu[maxn];
vector<int>E[maxn];
int dfn[maxn],ed[maxn];
int cnt;
int n,m,k;
int lowbit(int x){
return x&-x;
}
void add(int x,ll w){
while(x<=n){
val[x] += w;
x += lowbit(x);
}
}
ll query(int x){
ll res = 0;
while(x){
res += val[x];
x -= lowbit(x);
}
return res;
}
void dfs(int p,int fa){
dfn[p] = cnt++; add(dfn[p],valu[p]);
for(int i=0;i<(int)E[p].size();i++){
int v = E[p][i];
if(v!=fa){
dfs(v,p);
}
}
ed[p] = cnt-1;
}
int main(){
IOS
cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>valu[i];
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
E[u].push_back(v);
E[v].push_back(u);
}
cnt = 1;
dfs(k,0);
for(int i=1;i<=m;i++){
int op,p;
cin>>op>>p;
if(op==1){
ll w;
cin>>w;
add(dfn[p],w);
}else{
cout<<query(ed[p])-query(dfn[p]-1)<<endl;
}
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步