[HAOI2015]树上操作
[HAOI2015]树上操作https://www.luogu.org/problemnew/show/P3178
题目描述
有一棵点数为 \(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
说明
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。
树剖模板题,要连双向边.
单点修改\(+\)子树(区间)修改
区间查询(根节点到其它节点)
#define RG register
#define LL long long
#include<cstdio>
using namespace std;
const int N=1e5+5;
inline LL read()
{
RG LL x=0,w=1;RG char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
LL n,m,cnt,ct;
LL last[N],val[N],fa[N],top[N],size[N],dfn[N],rk[N],son[N];
LL sum[N<<2],lazy[N<<2];
struct edge{LL to,next;}e[N<<1];
void insert(LL u,LL v)
{
e[++cnt]=(edge){v,last[u]};last[u]=cnt;
e[++cnt]=(edge){u,last[v]};last[v]=cnt;
}
void dfs1(LL now)
{
size[now]=1;
for(RG LL i=last[now];i;i=e[i].next)
{
LL v=e[i].to;
if(v==fa[now])continue;
fa[v]=now;
dfs1(v);
size[now]+=size[v];
if(size[v]>size[son[now]])son[now]=v;
}
}
void dfs2(LL now,LL tp)
{
top[now]=tp;dfn[now]=++ct;rk[ct]=now;
if(son[now])dfs2(son[now],tp);
for(RG LL i=last[now];i;i=e[i].next)
{
LL v=e[i].to;
if(v==son[now]||v==fa[now])continue;
dfs2(v,v);
}
}
inline void Pushup(LL root){sum[root]=sum[root<<1]+sum[root<<1|1];}
void Build(LL root,LL l,LL r)
{
if(l==r){sum[root]=val[rk[l]];return;}
LL mid=(l+r)>>1;
Build(root<<1,l,mid);
Build(root<<1|1,mid+1,r);
Pushup(root);
}
inline void Pushdown(LL root,LL len)//len为区间长度
{
if(!lazy[root])return;
lazy[root<<1]+=lazy[root];
lazy[root<<1|1]+=lazy[root];
sum[root<<1]+=lazy[root]*(len-(len>>1));
sum[root<<1|1]+=lazy[root]*(len>>1);
lazy[root]=0;
}
void Modify(LL root,LL l,LL r,LL ll,LL rr,LL k)
{
if(ll<=l&&r<=rr)
{
sum[root]+=k*(r-l+1);
lazy[root]+=k;
return;
}
Pushdown(root,r-l+1);
LL mid=(l+r)>>1;
if(ll<=mid)Modify(root<<1,l,mid,ll,rr,k);
if(mid<rr) Modify(root<<1|1,mid+1,r,ll,rr,k);
Pushup(root);
}
LL Query(LL root,LL l,LL r,LL ll,LL rr)
{
if(ll<=l&&r<=rr)return sum[root];
Pushdown(root,r-l+1);
LL mid=(l+r)>>1,Sum=0;
if(ll<=mid)Sum+=Query(root<<1,l,mid,ll,rr);
if(mid<rr)Sum+=Query(root<<1|1,mid+1,r,ll,rr);
return Sum;
}
inline LL Query_Tree(LL now)
{
LL Sum=0;
while(now)//直到fa[top[now]]==0结束,表示top[now]==1-->根节点
{
Sum+=Query(1,1,n,dfn[top[now]],dfn[now]);
now=fa[top[now]];
}
return Sum;
}
int main()
{
n=read();
m=read();
for(RG LL i=1;i<=n;i++)val[i]=read();
for(RG LL i=1;i<n;i++)
{
int a=read(),b=read();
insert(a,b);
}
dfs1(1);
dfs2(1,1);
Build(1,1,n);
RG LL act,x,a;
while(m--)
{
act=read();
if(act==1)
{
x=read(),a=read();
Modify(1,1,n,dfn[x],dfn[x],a);
}
if(act==2)
{
x=read(),a=read();
Modify(1,1,n,dfn[x],dfn[x]+size[x]-1,a);
}
if(act==3)
{
x=read();
printf("%lld\n",Query_Tree(x));
}
}
return 0;
}