• gym102220H 差分+树状数组(区间修改和输出)


    这题目很有意思,让我学会了树状数组的差分,更加深刻理解了树状数组

    树状数组的差分写法

    void add(int x,int k){
        for (int i = x;i <= n;i += lowbit(i)) c[i] += k;
    }
    
    int sum(int x){
        int ans = 0;
        for (int i = x;i > 0;i -= lowbit(i)) ans += c[i];
        return ans;
    }
    
    int main(){
        add(l,x);
        add(r+1,-x);
        int zhi=sum(l)//就是a[l]的数值,前缀和。
    }

     二维差分模板

    ll c[4][N][N];
    void update(int k,int x,int y,ll z){
        k--;
        for(int i=x;i<=n;i+=lowbit(i))
        for(int j=y;j<=m;j+=lowbit(j))
            c[k][i][j]+=z;
    }
    
    ll sum(int k,int x,int y){
        ll ret=0;
        for(int i=x;i>=1;i-=lowbit(i))
        for(int j=y;j>=1;j-=lowbit(j))
            ret+=c[k][i][j];
        return ret;
    }
    
    ll get(int x,int y){
        return sum(0,x,y)*(ll)(x+1)*(y+1)-sum(1,x,y)*(ll)(y+1)-(ll)(x+1)*sum(2,x,y)+sum(3,x,y);
    }
    
    
    int main()
    {
           get(x2,y2)-get(x2,y1-1)-get(x1-1,y2)+get(x1-1,y1-1) ;
    
    
    
           update(1,x1,y1,w),update(1,x1,y2+1,-w);
           update(1,x2+1,y1,-w),update(1,x2+1,y2+1,w);
    
           update(2,x1,y1,w*x1),update(2,x2+1,y1,-w*(x2+1));
           update(2,x1,y2+1,-w*x1),update(2,x2+1,y2+1,w*(x2+1));
    
           update(3,x1,y1,w*y1),update(3,x2+1,y1,-w*y1);
           update(3,x1,y2+1,-w*(y2+1)),update(3,x2+1,y2+1,w*(y2+1));
    
           update(4,x1,y1,w*x1*y1),update(4,x2+1,y1,-w*(x2+1)*y1);
           update(4,x1,y2+1,-w*x1*(y2+1)),update(4,x2+1,y2+1,w*(x2+1)*(y2+1));
    }

    题意:

    很简单,输入n m

    给n个a[i],代表每栋楼要造的高度

    接下来m个操作

    输入op

    当op等于1的时候输入l,r,val

    区间l,r之间增加val高度

    当op等于2的时候输入l,r

    求区间l,r,最小的工作时长(这个我语文不好,举例吧 1 3 2需要3个工作时长 1 3 2 5需要6个工作时长,1 3 2 5 -> 0 2 1 4 -> 0 1 0 3 -> 0 0 0 3 -> 0 0 0 2 -> 0 0 0 1 -> 0 0 0 0)

    思路:

    差分树状数组

    a[i]原本的高度       b[i]=a[i]-a[i-1](前缀和) c[i]=b[i]>0?b[i]:0(后者比前者小,需要多出的工作时长)

    所以答案就是 a[l]+(c[i]求和(l<i<r))

    增加的val高度,a[l]用一个前缀和树状数组 add(val,l)add(-val,r+1)

    维护c[i]的树状数组特殊判断(tql这里的思考判断,做题的时候就是这里没处理好,于是代码写不出来)

    其实就是把每个建筑的递增高峰写出来,如果前者比后者大,后者那个位置就是0

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<queue>
    #include<stack>
    #include<algorithm>
    #include<stdio.h>
    #include<map>
    #include<set>
    #define ll long long
    #define lowbit(x) x&-x
    using namespace std;
    const int maxn=1e5+10;
    int n,m,a[maxn],b[maxn];
    ll cnt[maxn],cn[maxn];
    void add1(int zhi,int x){
        for(int i=x;i<=n;i+=lowbit(i)){
            cn[i]+=(ll)zhi;
        }
    }
    void add2(int zhi,int x){
        for(int i=x;i<=n;i+=lowbit(i)){
            cnt[i]+=(ll)zhi;
        }
    }
    ll geshu1(int x){
        if(x==0){return 0;}
        ll sum=0;
        for(int i=x;i>0;i-=lowbit(i)){
            sum+=cn[i];
        }
        return sum;
    }
    ll geshu2(int x){
        if(x==0){return 0;}
        ll sum=0;
        for(int i=x;i>0;i-=lowbit(i)){
            sum+=cnt[i];
        }
        return sum;
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d%d",&n,&m);
            for(int i=0;i<=n;i++){
                cnt[i]=0,cn[i]=0;
            }
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                b[i]=a[i]-a[i-1];int tt=(b[i]>0?b[i]:0);
                 add1(b[i],i);
                 add2(tt,i);
            }
            for(int i=1;i<=m;i++){
                int op;
                scanf("%d",&op);
                if(op==1){
                    int l,r,val;
                    scanf("%d%d%d",&l,&r,&val);
                    add1(val,l);add1(-val,r+1);//差分
                    if(b[l]<0){
                        int tt=-b[l];
                        if(tt<val){
                            add2(val-tt,l);
                        }
                    }
                    else{
                        add2(val,l);
                    }
                    b[l]+=val;
                    if(b[r+1]>=0){
                        int tt=min(b[r+1],val);
                        add2(-tt,r+1);
                    }
                    b[r+1]-=val;
                }
                else{
                    int l,r;
                    scanf("%d%d",&l,&r);
                    printf("%lld
    ",geshu2(r)-geshu2(l)+geshu1(l));
                }
            }
        }
        return 0;
    }

    看了扩展

    区间修改,单点输出

    单点修改,区间输出

    到区间修改和输出

    tql(看了Top_Spirit博客)

    数组cnt[n]  用来维护a[i]

    a[1]+a[2]+...+a[n]= (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n]) 

    = n*c[1] + (n-1)*c[2] +... +c[n]

    = n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n])

    再维护一个数组cnt2[n],cnt2[i] = (i-1)*c[i]

    a[1]+a[2]+...+a[n]=n*geshu1(cnt,n) - geshu2(cnt2,n)

       add1( l , val);
       add1(r + 1, -val);
       add2( l , (l - 1) * val);
       add2 (r + 1, r* (-z));



  • 相关阅读:
    c++ struct 使用
    c++数组、字符串操作
    c++ List、Vector、Stack、Queue使用
    十三、哈希表
    十二、234树
    十一、红黑树
    十、哈夫曼编码
    九、二叉树
    八、高级排序
    七、递归
  • 原文地址:https://www.cnblogs.com/luoyugongxi/p/12191649.html
Copyright © 2020-2023  润新知