• poj3468 线段树的懒惰标记


    题目链接:poj3468

    题意:给定一段数组,有两种操作,一种是给某段区间加c,另一种是查询一段区间的和

    思路:暴力的方法是每次都给这段区间的点加c,查询也遍历一遍区间,复杂度是n*n,肯定过不去,另一种思路是用线段树记录区间的和,每次查询的复杂度是lgn,修改不必更新到每个点,当某个区间全被修改时,我们可以给它加一个懒惰标记,表示这个区间的所有下面节点都需要更新,只是因为现在不需要使用而暂时没有更新。这样修改的复杂度也降到了lgn

    ac代码:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int maxn=1e5+10;
    long long num[maxn],sum[maxn*4],lazy[maxn*4];
    void pushdown(int rt,int len1,int len2)//向下更新懒惰标记
    {
        if(lazy[rt])
        {
            lazy[rt*2]+=lazy[rt];//注意是+=而不是=
            lazy[rt*2+1]+=lazy[rt];
            sum[rt*2]+=lazy[rt]*len1;
            sum[rt*2+1]+=lazy[rt]*len2;
            sum[rt]=sum[rt*2]+sum[rt*2+1];
            lazy[rt]=0;
        }
    }
    void build(int st,int en,int rt)
    {
        if(st==en)
        {
            sum[rt]=num[st];
            return;
        }
        build(st,(st+en)/2,rt*2);
        build((st+en)/2+1,en,rt*2+1);
        sum[rt]=sum[rt*2]+sum[rt*2+1];
    }
    void add(int l,int r,int c,int st,int en,int rt)
    {
         int md=(st+en)/2;
         if(l<=st&&r>=en)
         {
             lazy[rt]+=c;
             sum[rt]+=(en-st+1)*c;
             return ;
         }
         pushdown(rt,md-st+1,en-md);
         if(r>=md+1)
         {
             add(l,r,c,md+1,en,rt*2+1);
         }
         if(l<=md)
         {
             add(l,r,c,st,md,rt*2);
         }
         sum[rt]=sum[rt*2]+sum[rt*2+1];
    }
    long long quer(int l,int r,int rt,int st,int en)
    {
        long long res=0,md=(st+en)/2;
        if(l<=st&&r>=en)
            return sum[rt];
        pushdown(rt,md-st+1,en-md);
        if(r>=md+1)
            res+=quer(l,r,rt*2+1,md+1,en);
        if(l<=md)
            res+=quer(l,r,rt*2,st,md);
        return res;
    }
    int main()
    {
        char comd;
        int n,q;
        cin>>n>>q;
        for(int i=1;i<=n;i++)
            scanf("%lld",&num[i]);
        build(1,n,1);
        for(int i=1;i<=q;i++)
        {
            int l,r,c;
            cin>>comd;
            if(comd=='C')
            {
                scanf("%d %d %d",&l,&r,&c);
                add(l,r,c,1,n,1);
            }
            else
            {
                scanf("%d %d",&l,&r);
                printf("%lld
    ",quer(l,r,1,1,n));
            }
        }
        return 0;
    }
    

      

    提升题:hdu6315

    题意:有ab两个数组,有两种操作,一种是给a数组的一段区间加一,另一种是求a/b数组的累加和

    思路:只有当一段区间最大的a大于最小的b时,这段区间的答案才会发生改变,如果没有发生改变,那么我们就不必要去给子区间修改最大a的值。我们给这段区间加个懒惰标记就可以了,以后浏览到这个区间时我们再修改。复杂度为nlgn

    ac代码:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int maxn=1e5+10;
    int sum[maxn*4],maxa[maxn*4],minb[maxn*4],b[maxn],lazy[maxn*4];
    
    void pushup(int rt)
    {
        sum[rt]=sum[rt*2+1]+sum[rt*2];
        maxa[rt]=max(maxa[rt*2],maxa[rt*2+1]);
        minb[rt]=min(minb[rt*2],minb[rt*2+1]);
    }
    void pushdowm(int rt)
    {
        if(lazy[rt])
        {
            lazy[rt*2]+=lazy[rt];
            lazy[rt*2+1]+=lazy[rt];
            maxa[rt*2]+=lazy[rt];
            maxa[rt*2+1]+=lazy[rt];
            lazy[rt]=0;
        }
    }
    void build(int st,int en,int rt)
    {
        if(st==en)
        {
            minb[rt]=b[st];
            return;
        }
        build(st,(st+en)/2,rt*2);
        build((st+en)/2+1,en,rt*2+1);
        pushup(rt);
    }
    void add(int l,int r,int st,int en,int rt)
    {
        int md=(st+en)/2;
        pushdowm(rt);
        if(l<=st&&r>=en)
        {
            maxa[rt]++;
            if(maxa[rt]>=minb[rt])
            {
                if(st!=en)
                {
                    add(l,r,md+1,en,rt*2+1);
                    add(l,r,st,md,rt*2);
                    pushup(rt);
                }
                else
                {
                    while(maxa[rt]>=minb[rt])
                    {
                        minb[rt]+=b[st];
                        sum[rt]++;
                    }
                }
            }
            else
                lazy[rt]++;
            return;
        }
        else
        {
            if(l<=md)
                add(l,r,st,md,rt*2);
            if(r>=md+1)
                add(l,r,md+1,en,rt*2+1);
        }
        pushup(rt);
    }
    int quer(int l,int r,int st,int en,int rt)
    {
        pushdowm(rt);
        int md=(st+en)/2;
        if(l<=st&&r>=en)
            return sum[rt];
        int res=0;
        if(l<=md)
            res+=quer(l,r,st,md,rt*2);
        if(r>=md+1)
            res+=quer(l,r,md+1,en,rt*2+1);
        pushup(rt);
        return res;
    }
    int main()
    {
        int n,q;
        char comd[10];
        while(cin>>n>>q)
        {
            for(int i=0;i<maxn*4;i++)sum[i]=0,maxa[i]=0,minb[i]=0,lazy[i]=0;
            for(int i=0;i<maxn;i++)lazy[i]=0;
            
            for(int i=1; i<=n; i++)
                scanf("%d",&b[i]);
            build(1,n,1);
            for(int i=1; i<=q; i++)
            {
                int l,r;
                scanf("%s %d %d",&comd,&l,&r);
                if(comd[0]=='a')
                    add(l,r,1,n,1);
                else
                    printf("%d
    ",quer(l,r,1,n,1));
            }
        }
        return 0;
    }
    

      

     总结:懒惰标记可以解决一些区域修改问题

  • 相关阅读:
    documentFragment文档碎片
    OpenResty之resty.limit.count 模块介绍
    vue前端分页多条件搜索
    element ui Tree树形控件获取未全选父节点和子节点id
    如何使 pdf 文件在浏览器里面直接下载而不是打开
    关于本博客
    圆锥曲线基础知识点
    NOI2021游记
    20210716模拟赛
    计数+动态规划
  • 原文地址:https://www.cnblogs.com/carcar/p/9504060.html
Copyright © 2020-2023  润新知