做的时候经验不够,没有想到一种类似于差分的思想
观察操作:
操作一:单点修改,对子树影响:所有子树节点到根路径加a
操作二:子树所有节点增加a,可以看作先将所有节点到根的总权值加上a * dep[u],再减去a *
(dep[x]-1),数据结构维护一下即可
操作三:直接查询

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct segmenttree
{
    ll l,r,v,lazy,len;
}sgt1[8*100000+5],sgt2[8*100000+5];
ll lt[100005],nt[200005],ed[200005],val[100005],in[100005],out[100005],dep[100005],rk[100005],n,m,cnt,visittime;
void addedge(ll x,ll y)
{
    ed[++cnt]=y;nt[cnt]=lt[x];lt[x]=cnt;
}
void build1(ll p,ll l,ll r)
{
    sgt1[p].l=l;sgt1[p].r=r;sgt1[p].len=r-l+1;
    if(l==r)return ;
    ll mid=(l+r)>>1;
    build1(p<<1,l,mid);
    build1(p<<1|1,mid+1,r);
}
void build2(ll p,ll l,ll r)
{
    sgt2[p].l=l;sgt2[p].r=r;sgt2[p].len=r-l+1;
    if(l==r)return ;
    ll mid=(l+r)>>1;
    build2(p<<1,l,mid);
    build2(p<<1|1,mid+1,r);
}
void spread1(ll p)
{
    ll v=sgt1[p].lazy;sgt1[p].lazy=0;
    sgt1[p<<1].v+=sgt1[p<<1].len*v;
    sgt1[p<<1|1].v+=sgt1[p<<1|1].len*v;
    sgt1[p<<1].lazy+=v;sgt1[p<<1|1].lazy+=v;
}
void update1(ll p)
{
    sgt1[p].v=sgt1[p<<1].v+sgt1[p<<1|1].v;
}
void change1(ll p,ll l,ll r,ll d)
{
    if(sgt1[p].r<l||sgt1[p].l>r)return ;
    if(sgt1[p].lazy)spread1(p);
    if(l<=sgt1[p].l&&sgt1[p].r<=r)
    {
        sgt1[p].v+=sgt1[p].len*d;
        sgt1[p].lazy+=d;
        return ;
    }
    change1(p<<1,l,r,d);
    change1(p<<1|1,l,r,d);
    update1(p);
}
void spread2(ll p)
{
    ll v=sgt2[p].lazy;sgt2[p].lazy=0;
    sgt2[p<<1].v+=sgt2[p<<1].len*v;
    sgt2[p<<1|1].v+=sgt2[p<<1|1].len*v;
    sgt2[p<<1].lazy+=v;sgt2[p<<1|1].lazy+=v;
}
void update2(ll p)
{
    sgt2[p].v=sgt2[p<<1].v+sgt2[p<<1|1].v;
}
void change2(ll p,ll l,ll r,ll d)
{
    if(sgt2[p].r<l||sgt2[p].l>r)return ;
    if(sgt2[p].lazy)spread2(p);
    if(l<=sgt2[p].l&&sgt2[p].r<=r)
    {
        sgt2[p].v+=sgt2[p].len*d;
        sgt2[p].lazy+=d;
        return ;
    }
    change2(p<<1,l,r,d);
    change2(p<<1|1,l,r,d);
    update2(p);
}
ll getcnt1(ll p,ll l,ll r)
{
    if(sgt1[p].r<l||sgt1[p].l>r)return 0;
    if(l<=sgt1[p].l&&sgt1[p].r<=r)return sgt1[p].v;
    if(sgt1[p].lazy)spread1(p);
    return getcnt1(p<<1,l,r)+getcnt1(p<<1|1,l,r);
}
ll getcnt2(ll p,ll l,ll r)
{
    if(sgt2[p].r<l||sgt2[p].l>r)return 0;
    if(l<=sgt2[p].l&&sgt2[p].r<=r)return sgt2[p].v;
    if(sgt2[p].lazy)spread2(p);
    return getcnt2(p<<1,l,r)+getcnt2(p<<1|1,l,r);
}
void dfs(ll u,ll fa,ll dis)
{
    in[u]=++visittime;dep[u]=dep[fa]+1;rk[visittime]=u;
    change2(1,in[u],in[u],dis+val[u]);
    ll i;
    for(i=lt[u];i;i=nt[i])
    {
        ll v=ed[i];
        if(v==fa)continue;
        dfs(v,u,dis+val[u]);
    }
    out[u]=visittime;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    ll i;
    for(i=1;i<=n;i++)scanf("%lld",&val[i]);
    for(i=1;i<n;i++)
    {
        ll x,y;
        scanf("%lld%lld",&x,&y);
        addedge(x,y);
        addedge(y,x);
    }
    build1(1,1,n);build2(1,1,n);dfs(1,0,0);
    while(m--)
    {
        ll op,x;
        scanf("%lld%lld",&op,&x);
        if(op==3)printf("%lld\n",getcnt1(1,in[x],in[x])*dep[x]+getcnt2(1,in[x],in[x]));
        else
        {
            ll a;
            scanf("%lld",&a);
            if(op==1)change2(1,in[x],out[x],a);
            else
            {
                change1(1,in[x],out[x],a);
                change2(1,in[x],out[x],-(dep[x]-1)*a);
            }
        }
    }
    return 0;
}

区间修改区间查询,写复杂了
两颗线段树码量不敢想象