牛客小白月赛24—I.求和
题目描述
已知有(n)个节点,有(n-1)条边,形成一个树的结构。
给定一个根节点(k),每个节点都有一个权值,节点(i)的权值为(v_i)。
给(m)个操作,操作有两种类型:
1 a x : 表示将节点(a)的权值加上x
2 a : 表示求(a)节点的子树上所有节点的和(包括(a)节点本身)
输入描述
第一行给出三个正整数 (n,m,k),表示树的节点数、操作次数、和这颗树的根节点,
第二行给出(n)个正整数,第(i)个正整数表示第(i)个节点的权值(val_i)
下面(n-1)行每行两个正整数(u,v),表示边的两个端点
接下来(m)行,每行给出一个操作
输出描述
对于每个类型为 2 的操作,输出一行一个正整数,表示以 (a) 为根的子树的所有节点的权值和
样例输入
5 6 1
1 2 3 4 5
1 3
1 2
2 4
2 5
1 2 10
1 3 10
1 4 5
1 5 1
2 3
2 2
样例输出
13
27
备注
(1leq n,mleq 1e6,1leq k leq n)
(1leq u,v leq n)
(1leq a leq n)
(-1e6leq val_i,x leq 1e6)
建议使用scanf
读入
思路
DFS序加树状数组或者线段树维护前缀和
关于DFS序见:https://www.cnblogs.com/LeafLove/p/12730685.html
Code
#include <bits/stdc++.h>
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn = 2000111;
//graph_describe
int v[maxn],u[maxn],first[maxn],nxt[maxn],n,m,k,cnt;
ll val[maxn];
//dfs_order
int in[maxn],out[maxn],order;
//Tree_array
ll sum[maxn];
void add(int a,int b) {
cnt++;
u[cnt] = a; v[cnt] = b; nxt[cnt] = first[u[cnt]]; first[u[cnt]] = cnt;
return;
}
void dfs(int be,int fa) {
in[be] = ++order;
for(int i=first[be];i!=-1;i=nxt[i]) {
if(v[i] != fa) {
dfs(v[i],be);
}
}
out[be] = order;
return;
}
int lowbit(int x) { return x & (-x);}
ll get_ans(int x) {
ll ans = 0; while(x) {ans += sum[x];x -= lowbit(x);}
return ans;
}
void update(int x,ll w) {
while(x<=n) { sum[x] += w; x += lowbit(x);}
return;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) {
scanf("%lld", &val[i]);
}
for(int i=1;i<=n;i++) first[i] = -1;
for(int i=1;i<n;i++) {
int a,b;
scanf("%d%d",&a,&b);
add(a,b); add(b,a);
}
dfs(k,-1);
for(int i=1; i<=n; i++) {
update(in[i],val[i]);
}
while(m--){
int d,a;
scanf("%d%d",&d,&a);
if(d==1) {
ll x;
scanf("%lld",&x);
update(in[a],x);
}
else
{
printf("%lld
",get_ans(out[a]) - get_ans(in[a] - 1));
}
}
return 0;
}
第一次接触这种题时,真的是被这个做法深深吸引了