zoukankan      html  css  js  c++  java
  • 线段树 lazy

      线段树的写法,主要就分三大部分,也就是三个函数:建树(build)、操作(update),以及询问(query)。

      此代码为区间修改,区间查询。

      首先建树:

    void build(int L,int R,int now)
    {
        l[now]=L;
        r[now]=R;
        //lazy[now]=0;
        if(L==R)
        {
            scanf("%lld",&sum[now]);
            return ;
        }
        int mid=(L+R)/2;
        build(L,mid,now*2);
        build(mid+1,R,now*2+1);
        sum[now]=sum[now*2]+sum[now*2+1];
    }

      每一次将已有区间分为两部分,直到区间中剩下一个元素(为研究方便,假装存在这样的区间)。对于判断,只要(L==R)成立即可,此时便直接赋值,并返回。(l[]为now编号的区间的最左端的元素值,r[]为最右端元素值)

      那么对于修改:

    void update(int L,int R,ll d,int now)
    {
        if(l[now]==L&&r[now]==R)
        {
            lazy[now]+=d;
            return ;
        }
        sum[now]+=(long long)d*(R-L+1);
        int mid=(l[now]+r[now])/2;
        if(R<=mid)
            update(L,R,d,now*2);
        else if(mid<L)
            update(L,R,d,now*2+1);
        else
        {
            update(L,mid,d,now*2);
            update(mid+1,R,d,now*2+1);
        }
    }

      lazy数组是用来提高代码的效率,即当搜到应该修改的区间时(假设这个节点的序号为now),暂时不修改其sum[](记录值的数组),而是lazy[now]+=d(注意是+=不是=,我错了好几次),并且不再修改在now下方的节点。那么对于now上方的所有点,就都要加上我要修改的数的总数(即R-L)和我要增加的数的乘积(应该很好理解)。

      如果修改的区间在两个分支,那么我们分开继续搜就可以了,搜到了就操作lazy并且返回即可。

      注意:

        1.此时的R,L为将要修改的区间的两个端点,所以sum[]+=(R-L)*d。

        2.找到对应区间后,并没有修改sum[]值,所以以后再次碰到lazy的时候要修改其sum[],并且lazy下传(后话一会儿再说)。

        3.对于区间的平分问题:是要将整个区间平分,所以mid=(l[now]+r[now])/2,而不是R和L(对于每一个mid的求值,操作的都应是l[now]和r[now])。

      最后是求和:(加油马上就完事了~>\<)

    ll query(int L,int R,int now)
    {
        sum[now]+=(long long)(r[now]-l[now]+1)*lazy[now];
        lazy[now*2]+=lazy[now];
        lazy[now*2+1]+=lazy[now];
        lazy[now]=0;
        if(l[now]==L&&r[now]==R)
        {
            return sum[now];
        }
        int mid=(l[now]+r[now])/2;
        if(R<=mid)
            return query(L,R,now*2);
        else if(mid<L)
            return query(L,R,now*2+1);
        else
            return query(L,mid,now*2)+query(mid+1,R,now*2+1);
    }

      对于每一个搜到的点,都要加上它的lazy(反正没有lazy就是+0,没影响的),并且注意(敲黑板了):所有有lazy数组的点,它下面的点或多或少都会有本该操作却没有操作的点,它们本该加上的值就被记录在了现在搜到的这个点里,所以我们要将lazy数组下传(并且清零!)。

      注意:

        1.于lazy的下传:因为lazy存在说明这个点的整个区间都需要修改,所以为sum[]+=lazy[now]*(r[now]-l[now]+1),而不是R和L,它们只是我们要求和的点罢了,碰到它们,直接返回sum[]就可以。

        2.碰到一个sum[],即使有lazy也要(更要)修改其sum,因为之前的修改函数,对于正好的区间,是只记录了lazy而没修改sum。

      好啦好啦,该说的都说了,手也累了,希望能帮到大家!

      最后还是完整代码:

    #include<cstdio>
    using namespace std;
    #define maxn 100005
    #define ll long long
    int l[4*maxn],r[4*maxn];
    ll lazy[8*maxn],sum[4*maxn];
    void build(int L,int R,int now)
    {
        l[now]=L;
        r[now]=R;
        //lazy[now]=0;
        if(L==R)
        {
            scanf("%lld",&sum[now]);
            return ;
        }
        int mid=(L+R)/2;
        build(L,mid,now*2);
        build(mid+1,R,now*2+1);
        sum[now]=sum[now*2]+sum[now*2+1];
    }
    void update(int L,int R,ll d,int now)
    {
        if(l[now]==L&&r[now]==R)
        {
            lazy[now]+=d;
            return ;
        }
        sum[now]+=(long long)d*(R-L+1);
        int mid=(l[now]+r[now])/2;
        if(R<=mid)
            update(L,R,d,now*2);
        else if(mid<L)
            update(L,R,d,now*2+1);
        else
        {
            update(L,mid,d,now*2);
            update(mid+1,R,d,now*2+1);
        }
    }
    ll query(int L,int R,int now)
    {
        sum[now]+=(long long)(r[now]-l[now]+1)*lazy[now];
        lazy[now*2]+=lazy[now];
        lazy[now*2+1]+=lazy[now];
        lazy[now]=0;
        if(l[now]==L&&r[now]==R)
        {
            return sum[now];
        }
        int mid=(l[now]+r[now])/2;
        if(R<=mid)
            return query(L,R,now*2);
        else if(mid<L)
            return query(L,R,now*2+1);
        else
            return query(L,mid,now*2)+query(mid+1,R,now*2+1);
    }
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        build(1,n,1);
        while(m--)
        {
            int op,a,b;
            scanf("%d%d%d",&op,&a,&b);
            if(op==1)
            {
                ll x;
                scanf("%lld",&x);
                update(a,b,x,1);
            }
            else
                printf("%lld
    ",query(a,b,1));
        }
        return 0;
    }
  • 相关阅读:
    从V$SQL_PLAN中FORMAT执行计划
    使用 vmstat 监测系统性能
    aix 计算性内存和文件内存
    AIX 配置vncserver
    批量杀进程——xargs用途
    Aix命令大全
    linux查看文件个数命令
    查看Unix系统是32位还是64位
    UTF-8与UTF-8 BOM
    jdk1.9之前版本及jdk9安装配置环境变量
  • 原文地址:https://www.cnblogs.com/popo-black-cat/p/10022238.html
Copyright © 2011-2022 走看看