• 吊打线段树的超级树状数组


      你是否讨厌线段树那冗长的代码?你是否还在因为线段树的难调试而满头♂dark汗?那么,请不要错过!超级树状数组特价!只要998,只要998!

    ##¥……#……¥%……&%¥……ER#%$#$#^T%$^$%

      超级树状数组,其实是一种能够支持区间修改和区间查询的树状数组,和线段树相比,它的常数极小,不需要太多空间,代码量也少了很多(简直吊打线段树)

    1.树状数组

    既然是超级树状数组,那么就需要一个树状数组作为基础了。但是在真正实现时,只用到了lowbit()函数(所以说lowbit是树状数组的核心啊)

    2.准备工作

    首先,我们需要一个差分数组。

    设a[]数组为原数组,那么tree[](差分数组)定义为tree[i]=a[i]-a[i-1]

    猴子也能一眼看出的性质:a[i]=tree[1]+tree[2]+tree[3]+...+tree[i]

    3.区间查询

    (为什么先说查询呢。。)

    (1)查询区间1.....l的和

    sum[l]=a[1]+a[2]+...+a[l]

    其中a[i]=tree[1]+...+tree[i]

    那么我们可以很那啥的得到这个式子

    t1+t1+t2+t1+t2+t3+....+t1+t2+t3+....+tl(这啥玩意啊)

    如果你用数学角度去看的话,它是下面这个样子

    t1*l+t2*(l-1)+t3*(l-2)+....+tl*1

    如果你旁边坐着一位数竞大佬,ta会立刻看成这个样子

    l*(t1+t2+....+tl)-(t1*0+t2*1+...+tl*(l-1))

    然后我们惊奇的发现,这两个部分都是可以维护的

    所以我们就可以在输入时处理出一个差分数组和一个tree1[i]=tree[i]*(i-1)

    然后就可以查询了

    (2)查询l.....r的和

    类比前缀和处理

    (3)代码

    long long getsum(long long *arr,long long pos){
        long long sum=0;
        while(pos) sum+=arr[pos],pos-=lowbit(pos);
        return sum;
    }
    long long query(long long x,long long y){
        return y*getsum(d1,y)-(x-1)*getsum(d1,x-1)-(getsum(d2,y)-getsum(d2,x-1));
    }

    4.区间修改

    类比树状数组的区间修改

    void add(long long *arr,long long pos,long long x){
        while(pos<=n) arr[pos]+=x,pos+=lowbit(pos);
    }

    但是,由于tree和tree1的存在,修改需要改一下

    void change(long long l,long long r,long long x){
        add(d1,l,x);
        add(d1,r+1,-x);
        add(d2,l,x*(l-1));
        add(d2,r+1,-x*r);
    }

    若是将区间l-r加上x,就可以tree[l]+x,tree[r]-x,这样保证在计算a[i]时能让l-r内的数+x而其他不+x

    放代码

    #include<cstdio>
    #include<algorithm>
    //long long tree[100001];
    long long n,m;
    long long d1[100001];
    long long d2[100001];
    inline long long lowbit(long long x)
    {
        return x&-x;
    }
    /*void add(long long x,long long k)//μ¥μ?DT?? 
    {
        while(x<=n){
            tree[x]+=k;
            x+=lowbit(x);
        }
    }
    long long sum(long long pos)
    {//????2é?ˉ 
        long long sum=0;
        while(pos){
            sum+=tree[pos];
            pos-=lowbit(pos);
            return sum;
        }
    } 
    void add_ex(long long pos,long long x)
    {//????DT?? 
        while(pos<=n){
            detla[pos]+=x;
            pos+=lowbit(pos);
        }
    }
    void sum_ex(long long l,long long r,long long x)
    {
        add_ex(l,x);
        add(r+1,-x);
    }
    long long sum_ex(long long pos)//μ¥μ?2é?ˉ 
    {
        long long sum=0;
        while(pos){
            sum+=detla[pos];
            pos-=lowbit(pos);
        }
        return sum;
    }*/
    //ò???ê?????DT??+????2é?ˉ
    void add(long long *arr,long long pos,long long x){
        while(pos<=n) arr[pos]+=x,pos+=lowbit(pos);
    }
    void change(long long l,long long r,long long x){
        add(d1,l,x);
        add(d1,r+1,-x);
        add(d2,l,x*(l-1));
        add(d2,r+1,-x*r);
    }
    long long getsum(long long *arr,long long pos){
        long long sum=0;
        while(pos) sum+=arr[pos],pos-=lowbit(pos);
        return sum;
    }
    long long query(long long x,long long y){
        return y*getsum(d1,y)-(x-1)*getsum(d1,x-1)-(getsum(d2,y)-getsum(d2,x-1));
    }
    
    //ò???ê?×??μ
    /*
    void build(long long n){
        for(long long i=1;i<=n;i++){
            tree[i]=a[i];
            long long t=lowbit(i);
            for(long long j=1;j<t;j*=2)
            tree[i]=std::max(tree[i],tree[i-j]);
        }
    }
    void add(long long pos,long long x){
        a[pos]=x;
        while(pos<=n){
            tree[pos]=a[pos];
            long long t=lowbit(i);
            for(long long j=1;j<t;j++){
                tree[i]=std::max(tree[i],tree[i-j]);
            }
            pos+=lowbit(pos);
        }
    }
    long long query(long long l,long long r){
        long long ans=a[r];
        while(1){
            ans=std::max(ans,tree[r]);
            if(r==l)break;r--;
            while(r-l>=lowbit(r))ans=std::max(ans,tree[r]),r-=lowbit(r);
        }
        return ans;
    }
    */ 
    int main()//ê÷×′êy×é′ó?£°? 
    {
        scanf("%lld%lld",&n,&m);
        long long a,b=0;
        for(long long i=1;i<=n;i++){
            scanf("%lld",&a);
            b=a-b;
            add(d1,i,b);
            add(d2,i,(i-1)*b);
            b=a;
        }
        while(m--){
            long long op;
            scanf("%lld",&op);
            if(op==1){//???μ 
                long long x,y,z;
                scanf("%lld%lld%lld",&x,&y,&z);
                change(x,y,z);
    
            }else{
                long long x,y;//2é?ˉ 
                scanf("%lld%lld",&x,&y);
                printf("%lld
    ",query(x,y));
            }
        }
    }

    5.吊打线段树

    现在让我们统计一下超级树状数组的核心代码长度

    17行。。。。~~线段树你可以去死了~~

    让我们看一下超级树状数组和线段树在跑模板时的时间与空间

    ok线段树你真的可以当场去世了~

  • 相关阅读:
    Apache服务器的简单配置与安全策略
    Linux下的ICMP反弹后门:PRISM
    项目年度任务失败总结
    SpringBoot下配置Druid
    ftm国际化解决方案
    SpringBoot自动装配源码解析
    log4j到log4j2升级迁移方案
    Linux常用命令记录
    MySQL安装后无法用root用户访问的问题
    html实体命名
  • 原文地址:https://www.cnblogs.com/fxjrnh/p/9466423.html
Copyright © 2020-2023  润新知