1. 程式人生 > >吊打線段樹的超級樹狀數組

吊打線段樹的超級樹狀數組

查詢 真的 支持 bit 還在 ets arr 得到 cst

你是否討厭線段樹那冗長的代碼?你是否還在因為線段樹的難調試而滿頭♂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線段樹你真的可以當場去世了~

吊打線段樹的超級樹狀數組