P3178 [HAOI2015] 树上操作

P3178 [HAOI2015] 树上操作

题目描述

有一棵点数为 N 的树,以点 1 为根,且树有点权。然后有 M 个操作,分为三种:

  • 操作 1:把某个节点 x 的点权增加 a
  • 操作 2:把某个节点 x 为根的子树中所有点的点权都增加 a
  • 操作 3:询问某个节点 x 到根的路径中所有点的点权和。

提示

对于 100% 的数据,1N,M105,且所有输入数据的绝对值都不会超过 106

Solution:

十分有趣的 dfs 序+线段树。
我们思考一下贡献如何产生:我们用线段树维护每个点到根节点这条路径上的权值。

那么操作1就相当于在 [stx,edx] 上加上一个 a.

对于操作2:我们考虑这个操作对其子树内一个点 y 的贡献:(depx+depy+1)×a 我们将其拆开:(depx1)×a + depy×a 我们将前半段挂到 x 的子树下,后半段挂在 y 上。

实现:

我们维护一颗线段树,每个节点维护两类贡献val0,val1 分别表示对于这个点直接产生的贡献(操作1和操作二的前半部分)和与这个点的深度有关的贡献(操作2的后半部分)。

然后这题就做完了。

Code:

#include<bits/stdc++.h>
#define int long long
const int N=1e5+5;
using namespace std;
int n,m,tot;
int dep[N],st[N],ed[N],rid[N];
vector<int> E[N];
void dfs(int x,int fa)
{
st[x]=++tot;dep[x]=dep[fa]+1;
for(auto y : E[x])if(y!=fa)dfs(y,x);
ed[x]=tot;rid[st[x]]=x;
}
struct Segment_Tree{
struct Tree{
int tag[2],val[2];
}t[N<<2];
#define ls x<<1
#define rs x<<1|1
inline void add(int x,int k,int id){t[x].tag[id]+=k,t[x].val[id]+=k;}
inline void pushdown(int x)
{
if(t[x].tag[0]){add(ls,t[x].tag[0],0);add(rs,t[x].tag[0],0);t[x].tag[0]=0;}
if(t[x].tag[1]){add(ls,t[x].tag[1],1);add(rs,t[x].tag[1],1);t[x].tag[1]=0;}
}
void upd(int x,int l,int r,int L,int R,int k,int id)
{
if(L<=l&&r<=R){add(x,k,id);return;}
int mid=l+r>>1;pushdown(x);
if(L<=mid)upd(ls,l,mid,L,R,k,id);
if(mid<R)upd(rs,mid+1,r,L,R,k,id);
}
int query(int x,int l,int r,int pos)
{
if(l==r)return dep[rid[pos]]*t[x].val[0]+t[x].val[1];
int mid=l+r>>1;pushdown(x);
if(pos<=mid)return query(ls,l,mid,pos);
else return query(rs,mid+1,r,pos);
}
}T;
int a[N];
void work()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for(int i=2,x,y;i<=n;i++)
{
scanf("%lld%lld",&x,&y);
E[x].push_back(y);E[y].push_back(x);
}
dfs(1,0);
for(int i=1;i<=n;i++)T.upd(1,1,n,st[i],ed[i],a[i],1);
for(int i=1,opt,x,y;i<=m;i++)
{
scanf("%lld%lld",&opt,&x);
if(opt==1)
{
scanf("%lld",&y);
T.upd(1,1,n,st[x],ed[x],y,1);
}
if(opt==2)
{
scanf("%lld",&y);
T.upd(1,1,n,st[x],ed[x],-y*(dep[x]-1),1);
T.upd(1,1,n,st[x],ed[x],y,0);
}
if(opt==3)
{
int ans=T.query(1,1,n,st[x]);
printf("%lld\n",ans);
}
}
}
#undef int
int main()
{
//freopen("P3178.in","r",stdin);freopen("P3178.out","w",stdout);
work();
return 0;
}
posted @   liuboom  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示