题目描述
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a 。操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
输入输出格式
输入格式:
第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
输出格式:
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
输入输出样例
输入样例#1:
5 5 1 2 3 4 5 1 2 1 4 2 3 2 5 3 3 1 2 1 3 5 2 1 2 3 3输出样例#1:
6 9 13说明
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不
会超过 10^6 。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cassert>
#define DEBUG printf("passing [%s] in line %d\n", __FUNCTION__, __LINE__)
using namespace std;
typedef long long ll;
const int N = 500010;
int n, m, head[2 * N], rest[2 * N], to[2 * N], tot;
ll val[N];
void add(int u, int v) {
++ tot;
to[tot] = v;
rest[tot] = head[u];
head[u] = tot;
}
namespace segTree {
ll tag[N * 4], sum[N * 4];
int l[N * 4], r[N * 4];
void push(int id) {
if(id[tag]) {
(id << 1)[tag] += id[tag];
(id << 1 | 1)[tag] += id[tag];
id[sum] += id[tag] * (id[r] - id[l] + 1);
id[tag] = 0;
}
}
void update(int id) {
push(id);
push(id << 1);
push(id << 1 | 1);
id[sum] = (id << 1)[sum] + (id << 1 | 1)[sum];
}
void build(int id, int cl, int cr) {
int cm = (cl + cr) >> 1;
id[l] = cl;
id[r] = cr;
if(cl == cr) return;
build(id << 1, cl, cm);
build(id << 1 | 1, cm + 1, cr);
}
void modify(int id, int ql, int qr, ll val) {
push(id);
int ll = id[l], rr = id[r];
if(ll > qr || rr < ql) return;
if(ql <= ll && rr <= qr) {
id[tag] += val;
} else {
modify(id << 1, ql, qr, val);
modify(id << 1 | 1, ql, qr, val);
update(id);
}
}
ll query(int id, int ql, int qr) {
push(id);
int ll = id[l], rr = id[r];
if(ll > qr || rr < ql) return 0;
if(ql <= ll && rr <= qr) {
return sum[id];
} else {
return query(id << 1, ql, qr) + query(id << 1 | 1, ql, qr);
}
}
}
namespace splitTree {
int l[N], r[N], fa[N], sz[N], son[N], top[N], cnt;
void dfs1(int u) {
u[sz] = 1;
for(int i = head[u] ; i ; i = rest[i]) {
int v = to[i];
if(v != u[fa]) {
v[fa] = u;
dfs1(v);
u[sz] += v[sz];
if(u[son][sz] < v[sz]) {
u[son] = v;
}
}
}
}
void dfs2(int u, int tp) {
u[top] = tp;
u[l] = ++ cnt;
if(u[son]) dfs2(u[son], tp);
for(int i = head[u] ; i ; i = rest[i]) {
int v = to[i];
if(v != u[fa] && v != u[son]) {
dfs2(v, v);
}
}
u[r] = cnt;
}
void work() {
dfs1(1);
dfs2(1, 1);
segTree :: build(1, 1, n);
for(int i = 1 ; i <= n ; ++ i) {
segTree :: modify(1, i[l], i[l], i[val]);
}
}
}
namespace solve {
void addPoint(int u, ll a) {
segTree :: modify(1, u[splitTree :: l], u[splitTree :: l], a);
}
void addTree(int u, ll a) {
segTree :: modify(1, u[splitTree :: l], u[splitTree :: r], a);
}
void ask(int u) {
ll ans = 0;
while(u) {
ans += segTree :: query(1, u[splitTree :: top][splitTree :: l], u[splitTree :: l]);
u = u[splitTree :: top][splitTree :: fa];
}
printf("%lld\n", ans);
}
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1 ; i <= n ; ++ i) {
scanf("%lld", &val[i]);
}
for(int i = 1, u, v ; i < n ; ++ i) {
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
splitTree :: work();
for(int i = 1, op, x ; i <= m ; ++ i) {
scanf("%d", &op);
if(op == 1) {
ll a;
scanf("%d%lld", &x, &a);
solve :: addPoint(x, a);
} else if(op == 2) {
ll a;
scanf("%d%lld", &x, &a);
solve:: addTree(x, a);
} else {
scanf("%d", &x);
solve :: ask(x);
}
}
}