【题目描述】
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a 。操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
【输入格式】
第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类(1-3 ),之后接这个操作的参数(x 或者 x a) 。
【输出格式】
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
【样例输入】
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
【样例输出】
6
9
13
【题意分析】
太裸了吧,省选为什么会出这种裸题
不多说,暴力树剖直接上
不过记得开long long
虽然是裸题但是蒟蒻还是调了好久qwq
Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAX 100010
using namespace std;
typedef long long ll;
struct Front_Link_Star{
ll next,to;
}edge[MAX];
ll tree[MAX << 2],lazy[MAX << 2],top[MAX],depth[MAX],father[MAX],size[MAX];
ll id[MAX],rk[MAX],a[MAX],son[MAX],head[MAX],cnt,dfn,ans,res,n,m;
inline void Add_Edge(ll u,ll v){
edge[++cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt;
}
inline ll read(){
ll s=0,w=1;
char ch=getchar();
while (!isdigit(ch)){if (ch=='-')w=-1;ch=getchar();}
while (isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*w;
} //输入数据有负数
inline void push_down(ll now,ll tl,ll tr){
ll mid=(tl+tr) >> 1;
lazy[now << 1]+=lazy[now];
lazy[now << 1|1]+=lazy[now];
tree[now << 1]+=lazy[now]*(mid-tl+1);
tree[now << 1|1]+=lazy[now]*(tr-mid);
lazy[now]=0;
} //标记下传操作
inline void build(ll now,ll tl,ll tr){
if (tl==tr){
tree[now]=rk[tl];
return;
}
ll mid=(tl+tr) >> 1;
build(now << 1,tl,mid);
build(now << 1|1,mid+1,tr);
tree[now]=tree[now << 1]+tree[now << 1|1];
}
inline void update(ll now,ll tl,ll tr,ll left,ll right,ll change){
if (right<tl||tr<left)return;
if (left<=tl&&tr<=right){
lazy[now]+=change;
tree[now]+=change*(tr-tl+1);
return;
}
if (lazy[now])push_down(now,tl,tr);
ll mid=(tl+tr) >> 1;
update(now << 1,tl,mid,left,right,change);
update(now << 1|1,mid+1,tr,left,right,change);
tree[now]=tree[now << 1]+tree[now << 1|1];
}
inline void query(ll now,ll tl,ll tr,ll left,ll right){
if (right<tl||tr<left)return;
if (left<=tl&&tr<=right){
res+=tree[now];
return;
}
if (lazy[now])push_down(now,tl,tr);
ll mid=(tl+tr) >> 1;
query(now << 1,tl,mid,left,right);
query(now << 1|1,mid+1,tr,left,right);
} //以上为线段树
inline void Modify_Tree(ll x,ll y){
update(1,1,n,id[x],id[x]+size[x]-1,y);
} //修改以某个节点为根的子树
inline ll Query_Range(ll x,ll y){
ll ans=0;
while (top[x]!=top[y]){
if (depth[top[x]]<depth[top[y]])swap(x,y);
res=0;
query(1,1,n,id[top[x]],id[x]);
ans+=res;
x=father[top[x]];
}
if (depth[x]>depth[y])swap(x,y);
res=0;
query(1,1,n,id[x],id[y]);
ans+=res;
return ans;
} //路径查询(某个点到根节点)
inline void DFS1(ll now,ll fa,ll d){
father[now]=fa;
depth[now]=d;
size[now]=1;
ll maxson=-1;
for (register int i=head[now];i;i=edge[i].next){
ll v=edge[i].to;
if (v==fa)continue;
DFS1(v,now,d+1);
size[now]+=size[v];
if (maxson<size[v]){
son[now]=v;
maxson=size[v];
}
}
}
inline void DFS2(ll now,ll top_heavy){
top[now]=top_heavy;
id[now]=++dfn;
rk[dfn]=a[now];
if (!son[now])return;
DFS2(son[now],top_heavy);
for (register int i=head[now];i;i=edge[i].next){
ll v=edge[i].to;
if (v!=father[now]&&v!=son[now])DFS2(v,v);
}
} //预处理出各个数组
int main(){
n=read(),m=read();
for (register int i=1;i<=n;i++)
a[i]=read();
for (register int i=1;i<n;i++){
ll x=read(),y=read();
Add_Edge(x,y);
Add_Edge(y,x);
}
DFS1(1,0,1);
DFS2(1,1);
build(1,1,n);
while (m--){
ll type,x,y;
type=read();
if (type==1){
x=read(),y=read();
update(1,1,n,id[x],id[x],y);
}
if (type==2){
x=read(),y=read();
Modify_Tree(x,y);
}
if (type==3){
x=read();
printf("%lld\n",Query_Range(1,x));
}
}
return 0; //结束啦
}