链接:ac.nowcoder.com/acm/problem…
来源:牛客网
题目描述
已知有 n 个节点,有 n−1 条边,形成一个树的结构。
给定一个根节点 k,每个节点都有一个权值,节点i的权值为 vi。
给 m 个操作,操作有两种类型:
1 a x :表示将节点 a 的权值加上 x
2 a :表示求 a节点的子树上所有节点的和(包括 a 节点本身)
输入描述:
第一行给出三个正整数 n,m,k,表示树的节点数、操作次数、和这棵树的根节点.
第二行给出 n 个正整数,第 i个正整数表示第 i 个节点的权值 vali
下面 n−1 行每行两个正整数 u,v,表示边的两个端点
接下来 m 行,每行给出一个操作
输出描述:
对于每个类型为 2 的操作,输出一行一个正整数,表示以 a 为根的子树的所有节点的权值和
思路:因为题目要求我们做到单点修改, 区间求和的一个操作
显然树状数组能够满足要求
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int n,m,k,cnt,tot;
int l[N],r[N],a[N];
vector<int>edg[N];
template<class T>
struct NOW{
T c[N];
void init(int x)
{
for(int i = 1;i <= x;i ++)
c[i] = 0;
}
void modify(int x,int u)
{
for(;x <= n;x += (x&-x))
c[x] += u;
}
T query(int x)
{
int ans = 0;
for(;x;x -= (x&-x))
ans += c[x];
return ans;
}
};
NOW <int> c;
void dfs(int x,int u)
{
l[x] = ++cnt;
for(int i = 0;i < edg[x].size();i ++)
{
if(edg[x][i] == u)
continue;
dfs(edg[x][i],x);
}
r[x] = cnt;
}
void solve(){
scanf("%d%d%d",&n,&m,&k);
c.init(n);
for(int i = 1;i <= n;i ++)
{
scanf("%d",&a[i]);
}
for(int i = 1;i < n;i ++)
{
int x,y;
scanf("%d %d",&x,&y);
edg[x].push_back(y);
edg[y].push_back(x);
}
dfs(k,0);
for(int i = 1;i <= n;i ++)
{
c.modify(l[i],a[i]);
}
while(m--)
{
int ty;
scanf("%d",&ty);
if(ty == 1)
{
int x,y;
scanf("%d %d",&x,&y);
c.modify(l[x],y);
}
else
{
int x;
scanf("%d",&x);
int num = c.query(r[x]) - c.query(l[x]-1);
printf("%d\n",num);
}
}
}
signed main()
{
int _ = 1;
while(_--)
solve();
}