有一棵点数为 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 ) 。
N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6
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
/*
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,m,p,x,a,tot,head[100005],cnt,value[100005],u,v;
int son[100005],fa[100005],id[100005],size[100005],top[100005],dep[100005],mx[100005];
long long lazy[400005],sum[400005];
struct edge
{
int v,next;
} s[200005];
void pushdown(int now,int l,int r)
{
if(l==r) return;
int mid=(l+r)/2;
lazy[now*2]+=lazy[now];
lazy[now*2+1]+=lazy[now];
sum[now*2]+=lazy[now]*(mid-l+1);
sum[now*2+1]+=lazy[now]*(r-mid);
lazy[now]=0;
}
void add(int now,int l,int r,int x,int y,long long k)
{
if(lazy[now])
pushdown(now,l,r);
if(l==x&&y==r)
{
lazy[now]+=k;
sum[now]+=k*(r-l+1);
return;
}
int mid=(l+r)/2;
if(x<=mid) add(now*2,l,mid,x,min(mid,y),k);
if(y>mid) add(now*2+1,mid+1,r,max(mid+1,x),y,k);
sum[now]=sum[now*2]+sum[now*2+1];
}
long long query(int now,int l,int r,int x,int y)
{
if(lazy[now])
pushdown(now,l,r);
if(l==x&&y==r)
return sum[now];
int mid=(l+r)/2;
long long ans=0;
if(x<=mid)
ans+=query(now*2,l,mid,x,min(mid,y));
if(y>mid)
ans+=query(now*2+1,mid+1,r,max(x,mid+1),y);
return ans;
}
void Query(int x) //查询从x点到根结点所有点权和
{
long long ans=0;
while(top[x]!=1)
{
ans+=query(1,1,n,id[top[x]],id[x]);
x=fa[top[x]];
}
ans+=query(1,1,n,1,id[x]);
printf("%lld\n",ans);
return;
}
void dfs_1(int now,int father)
{
size[now]=1;
fa[now]=father;
for(int i=head[now];i!=0;i=s[i].next)
{
int to=s[i].v;
if(to==father) continue;
dfs_1(to,now);
size[now]+=size[to];
if(size[to]>size[son[now]])
son[now]=to;
}
}
void dfs_2(int now,int v)//树链剖分过程
{
top[now]=v;
id[now]=mx[now]=++tot;//记下在线段树的位置
if(son[now])
dfs_2(son[now],v), mx[now]=max(mx[now],mx[son[now]]);
//mx[now]记下最后在线段树中出现的位置
for(int i=head[now];i!=0;i=s[i].next)
{
int to=s[i].v;
if(to==son[now]||to==fa[now]) continue;
dfs_2(to,to);
mx[now]=max(mx[now],mx[to]);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&value[i]);
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&u,&v);
s[++cnt].v=v; s[cnt].next=head[u]; head[u]=cnt;
s[++cnt].v=u; s[cnt].next=head[v]; head[v]=cnt;
}
dfs_1(1,0);
dfs_2(1,1);
for(int i=1;i<=n;i++)//建立线段树
add(1,1,n,id[i],id[i],value[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&p,&x);
if(p==1) //某个节点 x 的点权增加 a 。
{
scanf("%d",&a);
add(1,1,n,id[x],id[x],a);
}
else if(p==2)
{
scanf("%d",&a);
//把某个节点 x 为根的子树中所有点的点权都增加 a
add(1,1,n,id[x],mx[x],a);
}
else
Query(x);
}
return 0;
}
原文:https://www.cnblogs.com/cutemush/p/11857723.html