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