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
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
9
13
HINT
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。
啊,终于在bzoj又找到一题水题!
此题都不需要思考,,就是树链剖分的模板题目了。
我们知道树链剖分中,为了让线段树能够维护,我们给了每个节点一个dfs序的编号。
要不下次写一篇树链剖分
所以为了更新子树,我们还需要记录一下每一个结点u的子树中dfs序最大元素的标号。
总而言之就是模板,没什么好说的。。。
哦哦,对了!
坑点就是要开long long的。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
ll 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;
}
const int
N=100005;
int Ecnt,cnt,n,m;
ll a[N];
struct Tree{
int sz,top,pre,son,tid,Max;
}tree[N];
struct Segment{
ll mark,sum;
}Seg[N<<2];
struct Edge{
int next,to;
}E[N<<1]; int head[N];
inline void add(int u,int v){
E[++Ecnt].next=head[u];
E[Ecnt].to=v;
head[u]=Ecnt;
}
inline void up(int u){
Seg[u].sum=Seg[u<<1].sum+Seg[u<<1|1].sum;
}
inline void down(int u,int l,int r){
if (Seg[u].mark){
int mid=(l+r)>>1;
Seg[u<<1].mark+=Seg[u].mark;
Seg[u<<1|1].mark+=Seg[u].mark;
Seg[u<<1].sum+=Seg[u].mark*(ll)(mid-l+1);
Seg[u<<1|1].sum+=Seg[u].mark*(ll)(r-mid);
Seg[u].mark=(ll)0;
}
}
void build(int x,int pre){
tree[x].sz=1; tree[x].pre=pre;
tree[x].son=0;
int maxx=0;
for (int i=head[x];i;i=E[i].next){
int j=E[i].to;
if (j==pre) continue;
build(j,x);
if (maxx<tree[j].sz){
maxx=tree[j].sz;
tree[x].son=j;
}
tree[x].sz+=tree[j].sz;
}
}
void dfs(int x,int ancestor){
tree[x].top=ancestor;
tree[x].tid=tree[x].Max=++cnt;
if (tree[x].son) dfs(tree[x].son,ancestor);
tree[x].Max=max(tree[x].Max,tree[tree[x].son].Max);
for (int i=head[x];i;i=E[i].next){
int j=E[i].to;
if (j==tree[x].pre || j==tree[x].son) continue;
dfs(j,j);
tree[x].Max=max(tree[x].Max,tree[j].Max);
}
}
void update(int id,int l,int r,int gl,int gr,ll num){
if (l>=gl && r<=gr){
Seg[id].mark+=num;
Seg[id].sum+=num*(ll)(r-l+1);
down(id,l,r);
return;
}
down(id,l,r);
int mid=(l+r)>>1;
if (gr<=mid) update(id<<1,l,mid,gl,gr,num); else
if (gl>mid) update(id<<1|1,mid+1,r,gl,gr,num);
else{
update(id<<1,l,mid,gl,mid,num);
update(id<<1|1,mid+1,r,mid+1,gr,num);
}
up(id);
}
ll query(int id,int l,int r,int gl,int gr){
down(id,l,r);
if (l>=gl && r<=gr) return Seg[id].sum;
int mid=(l+r)>>1;
if (gr<=mid) return query(id<<1,l,mid,gl,gr); else
if (gl>mid) return query(id<<1|1,mid+1,r,gl,gr);
else
return query(id<<1,l,mid,gl,mid)+query(id<<1|1,mid+1,r,mid+1,gr);
up(id);
}
ll solve(int x){
ll sum=(ll)0;
while (x!=0){
sum+=query(1,1,n,tree[tree[x].top].tid,tree[x].tid);
x=tree[tree[x].top].pre;
}
return sum;
}
int main(){
memset(Seg,0,sizeof(Seg));
n=read(),m=read();
for (int i=1;i<=n;i++) a[i]=read();
Ecnt=0; int x,y;
for (int i=1;i<n;i++){
x=read(),y=read();
add(x,y); add(y,x);
}
build(1,0);
cnt=0; dfs(1,0);
for (int i=1;i<=n;i++)
update(1,1,n,tree[i].tid,tree[i].tid,a[i]);
int opt; ll q;
while (m--){
opt=read();
if (opt==1){
x=read(),q=read();
update(1,1,n,tree[x].tid,tree[x].tid,q);
} else
if (opt==2){
x=read(),q=read();
update(1,1,n,tree[x].tid,tree[x].Max,q);
} else{
x=read();
printf("%lld\n",solve(x));
}
}
return 0;
}