Description
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
Input
第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1
行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中
第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
Output
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
Sample Input
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
Sample Output
6
9
13
HINT
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。
分析
我们注意到,对于一颗树而言,对他进行树剖之后呢,根于其子树的编号一定是连续的,那么我们就可以愉快的树剖了
代码
#include <bits/stdc++.h>
#define N 1000005
#define ll long long
int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
struct NOTE
{
int to,next;
}e[N << 1];
int cnt;
int next[N];
void insert(int x,int y)
{
e[++cnt] = (NOTE){y, next[x]}; next[x] = cnt;
e[++cnt] = (NOTE){x, next[y]}; next[y] = cnt;
}
struct TREE
{
ll sum;
ll lazy;
}t[N];
int n,m;
int top[N];
int fa[N],son[N];
int dep[N],size[N];
int pos[N],mx[N],pre[N];
void dfs1(int x,int d,int f)
{
dep[x] = d;
fa[x] = f;
size[x] = 1;
for (int i = next[x]; i; i = e[i].next)
{
int v = e[i].to;
if (v == f)
continue;
dfs1(v, d + 1, x);
size[x] += size[v];
if (!son[x] || size[v] > size[son[x]])
son[x] = v;
}
}
int tot;
void dfs2(int x,int k)
{
top[x] = k;
pos[x] = ++tot;
mx[x] = tot;
pre[pos[x]] = x;
if (!son[x])
return;
dfs2(son[x],k);
mx[x] = std::max(mx[x], mx[son[x]]);
for (int i = next[x]; i; i = e[i].next)
{
int v = e[i].to;
if (v != son[x] && v != fa[x])
{
dfs2(e[i].to, e[i].to);
mx[x] = std::max(mx[x], mx[v]);
}
}
}
void pushDown(int l,int r,int p)
{
if (l == r)
return ;
int mid = (l + r) >> 1;
ll tmp = t[p].lazy;
t[p].lazy = 0;
t[p * 2].lazy += tmp, t[p * 2 + 1].lazy += tmp;
t[p * 2].sum += tmp * (mid - l + 1);
t[p * 2 + 1].sum += tmp * (r - mid);
}
void add(int p,int l,int r,int x,int y,ll val)
{
if (t[p].lazy)
pushDown(l,r,p);
if (l == x && y == r)
{
t[p].lazy += val;
t[p].sum += val * (r - l + 1);
return;
}
int mid = (l + r) >> 1;
if (x <= mid)
add(p * 2, l, mid, x, std::min(y, mid), val);
if (y > mid)
add(p * 2 + 1, mid + 1, r, std::max(mid + 1, x), y, val);
t[p].sum = t[p * 2].sum + t[p * 2 + 1].sum;
}
ll query(int p,int l,int r,int x,int y)
{
if (t[p].lazy)
pushDown(l,r,p);
if (l == x && r == y)
return t[p].sum;
int mid = (l + r) >> 1;
ll ans = 0;
if (x <= mid)
ans += query(p * 2, l, mid, x, std::min(mid, y));
if (y > mid)
ans += query(p * 2 + 1, mid + 1, r, std::max(mid + 1, x), y);
return ans;
}
ll Query(int x)
{
ll ans = 0;
while (top[x] != 1)
{
ans += query(1, 1, n, pos[top[x]], pos[x]);
x = fa[top[x]];
}
ans += query(1, 1, n, 1, pos[x]);
return ans;
}
int v[N];
int main()
{
n = read(), m = read();
for (int i = 1; i <= n; i++)
v[i] = read();
for (int i = 1; i < n; i++)
{
int x = read(), y = read();
insert(x,y);
}
dfs1(1,1,0);
dfs2(1,1);
for (int i = 1; i <= n; i++)
add(1, 1, n, pos[i], pos[i], v[i]);
int opt,x,a;
for (int i = 1; i <= m; i++)
{
opt = read();
x = read();
if (opt == 1)
{
a = read();
add(1 , 1, n, pos[x], pos[x], a);
}
if (opt == 2)
{
a = read();
add(1 , 1, n, pos[x], mx[x], a);
}
if (opt == 3)
printf("%lld\n",Query(x));
}
}