zoukankan      html  css  js  c++  java
  • [bzoj 4034][HAOI 2015]树上操作

    Description

    有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
    操作,分为三种:
    操作 1 :把某个节点 x 的点权增加 a 。
    操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
    操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

    Input

    第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 
    行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中
    第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
     

    Output

    对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

     

    Sample Input

    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

    Sample Output

    6
    9
    13

    HINT

     对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。


    题解:

    首先看到操作三就能想到树链剖分吧?

    再看看题面就能想到线段树维护吧?

    然后就没难度了吧?

    考虑一下每个操作的做法:

    操作1:单点修改,直接在线段树上面修改就好

    操作2:把以x为根的子树+a,这是唯一有难度的一个地方。那么想一想我们是怎么剖分这棵树的——两次dfs,也就是说我们的树是按dfs序来构建的,再想想dfs序,它有一个很有趣的性质:

    一个子树的编号一定是连续的

    证明可以自己去找找。网上有的。

    那么当我们想到这个性质之后操作2就不难了,第一次dfs的时候我们已经维护出来一个siz数组表示该节点的子节点了,我们只需要对pos[x],pos[x]+siz[x]-1这个区间进行区间修改就可以了(pos数组是树上的节点在线段树中的编号)

    操作3:树链剖分的基本操作,爬到同一条重链上然后区间修改就好了

    Code:

    #include <cstdio>
    #include <cstring>
    #define ll long long
    #define inf 1<<30
    #define il inline 
    il ll max(ll x,ll y){return x>y?x:y;}
    il ll min(ll x,ll y){return x<y?x:y;}
    il ll abs(ll x){return x>0?x:-x;}
    il void swap(ll &x,ll &y){ll t=x;x=y;y=t;}
    il void read(ll &x){
        x=0;ll f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-f;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        x*=f;
    }
    il void print(ll x){if(x<0)putchar('-');x=abs(x);if(x>9)print(x/10);putchar(x%10+'0');}
    il void writeln(ll x){if(x<0)putchar('-');x=abs(x);print(x);putchar('
    ');}
    il void write(ll x){if(x<0)putchar('-');x=abs(x);print(x);putchar(' ');}
    using namespace std;
    /*===================Header Template=====================*/
    #define N 100010
    struct tree{ll l,r,sum,tag;}t[N<<2];
    struct data{ll to,next;}e[N<<1];
    ll n,m,root,pos[N],sz;
    ll cnt,head[N],v[N],v1[N];
    ll fa[N],siz[N],top[N],dep[N];
    void insert(ll u,ll v){
        e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;
        e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;
    }
    void dfs1(ll x){
        siz[x]=1;
        for(ll i=head[x];i;i=e[i].next){
            if(e[i].to==fa[x])continue;
            fa[e[i].to]=x;
            dep[e[i].to]=dep[x]+1;
            dfs1(e[i].to);
            siz[x]+=siz[e[i].to];
        }
    }
    void dfs2(ll x,ll topf){
        top[x]=topf;
        pos[x]=++sz;
        v1[sz]=v[x];
        ll k=0;
        for(ll i=head[x];i;i=e[i].next){
            if(dep[e[i].to]>dep[x]&&siz[e[i].to]>siz[k])k=e[i].to;
        }
        if(!k)return;
        dfs2(k,topf);
        for(ll i=head[x];i;i=e[i].next){
            if(k!=e[i].to&&dep[e[i].to]>dep[x])dfs2(e[i].to,e[i].to);
        }
    }
    void build(ll l,ll r,ll rt){
        t[rt].l=l;t[rt].r=r;
        ll mid=(l+r)>>1;
        if(l==r){return;}
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
        t[rt].sum=t[rt<<1].sum+t[rt<<1|1].sum;
    }
    void pushdown(ll ln,ll rn,ll rt){
        if(t[rt].tag){
            ll &x=t[rt].tag;
            t[rt<<1].tag+=x;
            t[rt<<1|1].tag+=x;
            t[rt<<1].sum+=x*ln;
            t[rt<<1|1].sum+=x*rn;
            x=0;
        }
    }
    void upd1(ll L,ll c,ll rt){
        ll l=t[rt].l,r=t[rt].r,mid=(l+r)>>1;
        if(l==r){t[rt].sum+=c;return;}
        pushdown(mid-l+1,r-mid,rt);
        if(L<=mid)upd1(L,c,rt<<1);
        else upd1(L,c,rt<<1|1);
        t[rt].sum=t[rt<<1].sum+t[rt<<1|1].sum;
    }
    void upd2(ll L,ll R,ll c,ll rt){
        ll l=t[rt].l,r=t[rt].r,mid=(l+r)>>1;
        if(L<=l&&r<=R){t[rt].sum+=(r-l+1)*c;t[rt].tag+=c;return;}
        pushdown(mid-l+1,r-mid,rt);
        if(L<=mid)upd2(L,R,c,rt<<1);
        if(R>mid)upd2(L,R,c,rt<<1|1);
        t[rt].sum=t[rt<<1].sum+t[rt<<1|1].sum;
    }
    ll query(ll L,ll R,ll rt){
        ll l=t[rt].l,r=t[rt].r,mid=(l+r)>>1,ans=0;
        if(L<=l&&r<=R)return t[rt].sum;
        pushdown(mid-l+1,r-mid,rt);
        if(L<=mid)ans+=query(L,R,rt<<1);
        if(R>mid)ans+=query(L,R,rt<<1|1);
        return ans; 
    }
    ll solve_query(ll x,ll y){
        ll sum=0;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]])swap(x,y);
            sum+=query(pos[top[x]],pos[x],1);
            x=fa[top[x]];
        }
        if(pos[x]>pos[y])swap(x,y);
        sum+=query(pos[x],pos[y],1);
        return sum;
    }
    int main(){
        read(n);read(m);
        for(ll i=1;i<=n;i++)read(v[i]);
        for(ll i=1;i<n;i++){
            ll x,y;
            read(x);read(y);
            insert(x,y);
        }
        dfs1(1);dfs2(1,1);
        build(1,n,1);
        for(ll i=1;i<=n;i++)upd1(pos[i],v[i],1);
        while(m--){
            ll pd,x,y;
            read(pd);read(x);
            if(pd==1){
                read(y);
                upd1(pos[x],y,1);
            }else if(pd==2){
                read(y);
                upd2(pos[x],pos[x]+siz[x]-1,y,1);
            }else if(pd==3){
                writeln(solve_query(x,1));
            }
        }
        return 0;
    }

    转载请注明出处:https://www.cnblogs.com/henry-1202/p/9129614.html

  • 相关阅读:
    Windows设置VMware开机自动启动,虚拟机也启动
    PHP中unset,array_splice删除数组中元素的区别
    Linux 出现 E325:ATTENTION swap
    Linux中的info指令
    java创建线程的三种方式——附源码说明
    JVM类加载过程
    java实现责任链模式的小demo
    讲讲java中线程池的实现
    将原型模式和建造者模式结合起来耍一耍
    一个简单的单例模式Demo
  • 原文地址:https://www.cnblogs.com/henry-1202/p/9129614.html
Copyright © 2011-2022 走看看