[HAOI2015]树上操作

Posted faced

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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],rank[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;rank[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[rank[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;
}

以上是关于[HAOI2015]树上操作的主要内容,如果未能解决你的问题,请参考以下文章

树上操作[HAOI 2015]

BZOJ4034: [HAOI2015]树上操作

[HAOI2015]树上操作

[BZOJ4034][HAOI2015]树上操作

BZOJ 4034 HAOI2015 树上操作

cogs 1963. [HAOI 2015] 树上操作 树链剖分+线段树