• 区间操作---树状数组&&线段树


    涉及区间操作的一些套路必须要会呀

    区间加减为了偷懒能不写线段树so我选择树状数组!!

    但是区间乘除,最大值我想了想还是用线段树分块吧。

    树状数组:

    这里用网上的一张图:

    这里灰色数组是原本的数组(a[i])红色数组则是树状数组(c[i])这里直接给出结论:

    c[i]=a[i-2^k+range[1,2^k]]

    k是i的二进制位从低到高位连续0的个数

    与a[i]有关的 c[i+2^(k+j)] 且 i+2^(j+k)<n

    这样就很好实现单点更改,区间查询了。

    void lowbit(int x)
    {
        return x&(-x);//求2^x
    }
    void updata(int x,int val)//在x处加val
    {
        while(x<=n)
        {
            c[i]+=val;
            x+=lowbit(x);//x在不断变化要不断求lowbit 
        }
    }
    int getsum(x)//求a[1~x]的和 
    {
        int ans=0;
        while(x)
        {
            ans+=c[x];
            x-=lowbit(x); 
        } 
        return ans;
    }

      

    区间查询怎么搞呢?

    这里可以用到差分数组

    比如在a=[1,5,7,2,3,7,1]则有的差分数组d=[1,4,2,-5,1,4,-6](a[i]-a[i-1)a[0]=0,sum[d[1~i]]=a[i]

    在区间x,y全部加k则可以在d[x]+k,d[y+1]-k 则在求1~i i in range[x,y]这部分区间的和即可得到a[i]+k。这样就变成了单点修改区间查询了

    这样实际上a数组是没有用的,’a‘则变成了d数组,对d数组构造树状数组c

    这样就实现了区间修改,单点查询:

    单点查询模板 https://www.luogu.com.cn/problem/P3368

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int N=5e5+6;
    int n,m,q,p,w,x,ans,a[N],c[N];
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int x,int val)//x点+val值 
    {
        while(x<=n)
        {
            c[x]+=val;
            x+=lowbit(x);//x在不断变化要不断求lowbit 
        }
    }
    int getsum(int x)//求'a'[1~x]的和 
    {
        int res=0;
        while(x)
        {
            res+=c[x];
            x-=lowbit(x); 
        } 
        return res;
    }
    int main()
    {
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            update(i,a[i]-a[i-1]);//建立差分数组d 
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&x);
            if(x==1)
            {
                scanf("%d %d %d",&q,&p,&w);//q,p区间+w 
                update(q,w);
                update(p+1,-w);
            }
            else
            {
                scanf("%d",&q);
                ans=getsum(q); //求d的1~q的和即为a[q] 
                printf("%d
    ",ans);
            }
        } 
        return 0;    
    } 

    区间修改怎么搞呢?

    sum[a[1~n]]=d[1]+d[1~2]+...+d[1~n]=n*d[1]+(n-1)d[2]+...+d[n]=n*d[1~n]-(0*d[1]+1*d[2]+...+(n-1)*d[n])(在树状数组里就是另一种表示,这里只是普通数组表示)

    可见两部分变量可写成=n*sum1[n]-sum2[n](又是区间求和了)

    永远要记住求谁的和构建谁的树状数组然后再树状数组求和,因为优化是树状数组求和造成的

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int N=5e5+6;
    int n,m,q,p,w,x,ans,a[N],c1[N],c2[N];
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int x,int val)//x点+val值 
    {
        int i=x;
        while(i<=n)
        {
            c1[i]+=val;//c1就是d数组的树状数组 
            c2[i]+=(x-1)*val;    //c2是d[i]*(i-1)的树状数组 
            i+=lowbit(i);//i要不断变化所以要不断求lowbit 
        }
    }
    int getsum(int x)//求a[1~x]的和 
    {
        int res=0,res1=0,res2=0,i=x;
        while(i)
        {
            res1+=c1[i]*x;//求d[1~x](sum1)根据分配律可直接求得sum1*n 
            res2+=c2[i];//求sum2 
            //res+=c[i]*x-c2[i];就是单纯的求a[1~x]
            i-=lowbit(i); 
        } 
        res=res1-res2;//a[1~x]
        return res;
    }
    int main()
    {
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            update(i,a[i]-a[i-1]);//建立
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&x);
            if(x==1)
            {
                scanf("%d %d %d",&q,&p,&w);//q,p区间+w 
                update(q,w);
                update(p+1,-w);
            }
            else
            {
                scanf("%d",&q);
                ans=getsum(q)-getsum(q-1);  
                printf("%d
    ",ans);
            }
        } 
        return 0;    
    } 

      

    线段树整合:

    忘不了的线段树开四倍

    区间加减,乘除,最大值:

    代码还是过那个模板题的

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int N=5e5+6;
    int n,m,q,p,w,x,ans,a[N];
    struct node
    {
        int sum,add,mul;//add,mul是lazy_tag 
    }t[N*4];
    void push_down(int p)
    {
        t[p].sum=t[p*2].sum+t[p*2+1].sum;//%mod    
        return ;
    } 
    void built(int p,int l,int r)
    {
        t[p].add=0;
        t[p].mul=1;
        if(l==r)
        {
            t[p].sum=a[l];
            //mod;
            return ;
        }
        int mid=(l+r)/2;
        built(p*2,l,mid);
        built(p*2+1,mid+1,r); 
        push_down(p);
        return ;
    }
    int askmin(int p,int qx,int zx,int gl,int gr)
    {
        if(qx>=gl&&zx<=gl)
        {
            return t[p].minn;
        }
        int mid=(qx+zx)/2,ans=9999999;
        if(gl<=mid)
            ans=min(ans,askmin(p*2,qx,mid,gl,gr));
        if(gr>mid)
            ans=min(ans,askmin(p*2+1,mid+1,zx,gl,gr));
            return ans;
    } 
    void push_tag(int p,int l,int r)
    {
        if(t[p].mul!=1)//必须先更新乘法 
        {
            t[p*2].sum*=t[p].mul;//mod
            t[p*2+1].sum*=t[p].mul;
            t[p*2].add*=t[p].mul;//mod
            t[p*2+1].add*=t[p].mul; 
            t[p*2].mul*=t[p].mul;//mod
            t[p*2+1].mul*=t[p].mul;
            t[p].mul=1;
        }
        if(t[p].add)//加不会对乘的tag产生影响 
        {
            int mid=(l+r)/2;
            t[p*2].add+=t[p].add;//mod
            t[p*2+1].add+=t[p].add;
            t[p*2].sum+=(mid-l+1)*t[p].add;//mod
            t[p*2+1].sum+=(r-mid)*t[p].add;
            t[p].add=0;
        }
        return ;
    }
    void add(int p,int qx,int zx,int gl,int gr,int k)//区间加减
    {
        if(qx>=gl&&zx<=gr)
        {
            t[p].sum+=(zx-qx+1)*k;
            t[p].add+=k;//mod
            return ;
        }
        int mid=(qx+zx)/2;
        push_tag(p,qx,zx);
        if(gl<=mid)
            add(p*2,qx,mid,gl,gr,k);
        if(gr>mid)
            add(p*2+1,mid+1,zx,gl,gr,k);
        push_down(p);
        return ;
    } 
    void mult(int p,int qx,int zx,int gl,int gr,int k)//区间乘 
    {
        if(qx>=gl&&zx<=gr)
        {
            t[p].sum*=k;
            t[p].add*=k;
            t[p].mul*=k;//mod
            return ;
        }
        int mid=(qx+zx)/2;
        if(gl<=mid)
            mult(p*2,qx,mid,gl,gr,k);
        if(gr>mid)
            mult(p*2+1,mid+1,zx,gl,gr,k); 
    }
    int ask(int p,int qx,int zx,int gl,int gr)
    {
        if(qx>=gl&&zx<=gr)
            return t[p].sum;//mod
        int ans=0,mid=(qx+zx)/2;
        push_tag(p,qx,zx);
        if(gl<=mid)
            ans+=ask(p*2,qx,mid,gl,gr);
        if(gr>mid)
            ans+=ask(p*2+1,mid+1,zx,gl,gr);
        return ans;
    }    
    int main()
    {
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        built(1,1,n);
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&x);
            if(x==1)
            {
                scanf("%d %d %d",&q,&p,&w);//q,p区间+w 
                add(1,1,n,q,p,w);
            }
            if(x==2)
            {
                scanf("%d",&q);  
                printf("%d
    ",ask(1,1,n,q,q));
            }
        } 
        return 0;    
    } 

      

  • 相关阅读:
    二分查找
    选择排序算法
    1.2.2凑零钱问题(暴力递归+动态规划)
    1.2.1斐波那契数列4种解法(暴力递归+动态规划)
    二维数组查找
    插入排序算法
    母亲的爱今天是母亲节,和妈妈通话后回忆起往事
    冒泡排序算法
    Pandas使用入门
    pandas.contact函数
  • 原文地址:https://www.cnblogs.com/cherrypill/p/12378028.html
Copyright © 2020-2023  润新知