zoukankan      html  css  js  c++  java
  • [BZOJ 4034][HAOI2015]树上操作(欧拉序列+线段树)

    Description

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

    Solution

    本来是一道裸的树剖,不过其实是可以欧拉序列搞一搞的

    对于每个节点在dfs序中记录2次,入栈的时候加上,出栈的时候减去

    那么一个点到根的路径上的信息就是dfs序里它第一次出现的位置的前缀和

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #define MAXN 100005
    using namespace std;
    typedef long long LL;
    int n,m,w[MAXN],head[MAXN],cnt=0,q[MAXN*2],in[MAXN],out[MAXN],dfn_clock=0;
    struct Node1
    {
        int next,to;
    }Edges[MAXN*2];
    struct Node2
    {
        int l,r,num;
        LL sum,lazy;
    }t[MAXN*8];
    int read()
    {
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    void addedge(int u,int v)
    {
        Edges[++cnt].next=head[u];
        head[u]=cnt;
        Edges[cnt].to=v;
    }
    void dfs(int u,int f)
    {
        q[++dfn_clock]=u;in[u]=dfn_clock;
        for(int i=head[u];~i;i=Edges[i].next)
        {
            int v=Edges[i].to;
            if(v==f)continue;
            dfs(v,u);
        }
        q[++dfn_clock]=-u;out[u]=dfn_clock;
    }
    void update(int idx)
    {
        t[idx].sum=t[idx<<1].sum+t[idx<<1|1].sum;
    }
    void pushdown(int idx)
    {
        if(t[idx].lazy&&t[idx].l!=t[idx].r)
        {
            t[idx<<1].lazy+=t[idx].lazy;
            t[idx<<1|1].lazy+=t[idx].lazy;
            t[idx<<1].sum+=1LL*t[idx].lazy*t[idx<<1].num;
            t[idx<<1|1].sum+=1LL*t[idx].lazy*t[idx<<1|1].num;
            t[idx].lazy=0;
        }
    }
    void build(int idx,int l,int r)
    {
        t[idx].l=l,t[idx].r=r;
        if(l==r)
        {
            t[idx].lazy=0;
            if(q[l]>0){t[idx].sum=w[q[l]],t[idx].num=1;return;}
            else{t[idx].sum=-w[-q[l]],t[idx].num=-1;return;}
        }
        int mid=(l+r)>>1;
        build(idx<<1,l,mid),build(idx<<1|1,mid+1,r);
        t[idx].num=t[idx<<1].num+t[idx<<1|1].num;
        update(idx);
    }
    void add(int idx,int l,int r,int v)
    {
        if(l<=t[idx].l&&r>=t[idx].r)
        {
            t[idx].lazy+=v;
            t[idx].sum+=1LL*t[idx].num*v;
            return;
        }
        pushdown(idx);
        int mid=(t[idx].l+t[idx].r)>>1;
        if(r<=mid)add(idx<<1,l,r,v);
        else if(l>mid)add(idx<<1|1,l,r,v);
        else add(idx<<1,l,r,v),add(idx<<1|1,l,r,v);
        update(idx);
    }
    LL query(int idx,int l,int r)
    {
        if(l<=t[idx].l&&r>=t[idx].r)return t[idx].sum;
        pushdown(idx);
        int mid=(t[idx].l+t[idx].r)>>1;
        if(r<=mid)return query(idx<<1,l,r);
        else if(l>mid)return query(idx<<1|1,l,r);
        else return query(idx<<1,l,r)+query(idx<<1|1,l,r);
    }
    int main()
    {
        n=read(),m=read();
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++)w[i]=read();
        for(int i=1;i<n;i++)
        {
            int u=read(),v=read();
            addedge(u,v);
            addedge(v,u);
        }
        dfs(1,0),build(1,1,dfn_clock);
        for(int i=1;i<=m;i++)
        {
            int opt=read(),x=read(),a;
            if(opt==1)a=read(),add(1,in[x],in[x],a),add(1,out[x],out[x],a);
            else if(opt==2)a=read(),add(1,in[x],out[x],a);
            else printf("%lld
    ",query(1,1,in[x]));
        }
        return 0;
    }
  • 相关阅读:
    例6-5
    例7-1+7-2
    例6-2+6-3
    习题二(3)
    习题二(1)
    课堂作业4
    课堂作业3
    实验三 利用循环计算n个圆柱体体积。
    实验三 编写求圆面积的程序,要求当输入的半径r<=0时,提示输入错误,要求r为浮点型,r的数值是动态的由键盘输入;
    心得3
  • 原文地址:https://www.cnblogs.com/Zars19/p/6924766.html
Copyright © 2011-2022 走看看