题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4034;
题目大意:有一棵以1为根节点的树,每个节点上都有权值,接下来有m次操作,每次操作有三种操作:
1 x a :令x节点的权值增加a;
2 x a :令以x为根节点的子树上所有节点的权值都增加a;
3 x :查询从x到根节点1的所有节点的权值和。
题目思路:刚刚开始学树链剖分,看了看一些博客对树链剖分也有一些了解了。这题是一个树链剖分的裸题,根据dfs序的性质,子树的节点的dfs序号是连续的,这样可以很简单的用线段来维护第一和第二种操作了,第三个查询就用到了树链剖分的一些性质,查询x到根节点1的所有权值和时可以把根节点1到x的这一条链分解成x到top[x](top[x]就是x所在链的端点),fa[top[x]]到top[fa[top[x]]],...,直到遍历到根节点1为止,然后用线段树的区间查询将这几部分的和加起来就行了,具体操作看代码吧。(ps:这题蛮不错的,很适合树链剖分的新学者,可以更加清楚的理解树链剖分)。
#include <bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long LL;
const int MX = 1e5+7;
int n,m;
int a[MX];
int in[MX],out[MX],tot;
int top[MX],sz[MX],son[MX],dep[MX],fa[MX];
LL sum[MX<<2],lazy[MX<<2];
vector<int>E[MX];
void init(){
memset(son,0,sizeof(son));
memset(fa,0,sizeof(fa));
for(int i = 0;i <= n;i++) E[i].clear();
tot = 0;
}
void add_edge(int u,int v){
E[u].push_back(v);
E[v].push_back(u);
}
void dfs1(int u){
sz[u] = 1;
for(int i = 0;i < E[u].size();i++){
int v = E[u][i];
if(v == fa[u]) continue;
fa[v] = u;dep[v] = dep[u] + 1;
dfs1(v);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u,int tp){
in[u] = ++tot;top[u] = tp;
if(son[u]) dfs2(son[u],tp);
for(int i = 0;i < E[u].size();i++){
int v = E[u][i];
if(v == fa[u] || v == son[u]) continue;
dfs2(v,v);
}
out[u] = tot;
}
void push_up(int rt){
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void push_down(int rt,int m){
if(lazy[rt]){
lazy[rt<<1] += lazy[rt];
lazy[rt<<1|1] += lazy[rt];
sum[rt<<1] += lazy[rt]*(m-(m>>1));
sum[rt<<1|1] += lazy[rt]*(m>>1);
lazy[rt] = 0;
}
}
void build(int l,int r,int rt){
sum[rt] = lazy[rt] = 0;
if(l == r) return;
int m = (l+r)>>1;
build(lson);
build(rson);
push_up(rt);
}
void update(int L,int R,LL d,int l,int r,int rt){
if(L <= l && r <= R){
sum[rt] += (LL)d*(r-l+1);
lazy[rt] += d;
return;
}
push_down(rt,r-l+1);
int m = (l+r)>>1;
if(L <= m) update(L,R,d,lson);
if(R > m) update(L,R,d,rson);
push_up(rt);
}
LL query(int L,int R,int l,int r,int rt){
if(L <= l && r <= R) return sum[rt];
push_down(rt,r-l+1);
int m = (l + r) >> 1;
LL res = 0;
if(L <= m) res += query(L,R,lson);
if(R > m) res += query(L,R,rson);
return res;
}
LL solve(int x){
LL res = 0;
while(top[x] != 1){
res += query(in[top[x]],in[x],1,n,1);
x = fa[top[x]];
}
res += query(1,in[x],1,n,1);
return res;
}
int main(){
while(~scanf("%d%d",&n,&m)){
init();
build(1,n,1);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
for(int i = 1;i < n;i++){
int u,v;scanf("%d%d",&u,&v);
add_edge(u,v);
}
dfs1(1);dfs2(1,1);
for(int i = 1;i <= n;i++)
update(in[i],in[i],a[i],1,n,1);
while(m--){
int op;scanf("%d",&op);
if(op == 1){
int x,d;scanf("%d%d",&x,&d);
update(in[x],in[x],d,1,n,1);
} else if(op == 2){
int x,d;scanf("%d%d",&x,&d);
update(in[x],out[x],d,1,n,1);
} else{
int x;scanf("%d",&x);
printf("%lld\n",solve(x));
}
}
}
return 0;
}