P3178 [HAOI2015] 树上操作

P3178 [HAOI2015] 树上操作

题目描述

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

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

提示

对于 \(100\%\) 的数据,\(1\le N,M\le10^5\),且所有输入数据的绝对值都不会超过 \(10^6\)

Solution:

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

那么操作1就相当于在 \([st_x,ed_x]\) 上加上一个 \(a\).

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

实现:

我们维护一颗线段树,每个节点维护两类贡献\(val_0 ,val_1\) 分别表示对于这个点直接产生的贡献(操作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 @ 2025-02-19 14:10  liuboom  阅读(35)  评论(0)    收藏  举报