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

  你是否讨厌线段树那冗长的代码?你是否还在因为线段树的难调试而满头♂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\n",query(x,y));
        }
    }
}

 

5.吊打线段树

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

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

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

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

posted @ 2018-08-13 09:53  Usmireko  阅读(764)  评论(1编辑  收藏  举报