你是否討厭線段樹那冗長的代碼?你是否還在由於線段樹的難調試而滿頭♂dark汗?那麼,請不要錯過!超級樹狀數組特價!只要998,只要998!數組
##¥……#……¥%……&%¥……ER#%$#$#^T%$^$%函數
超級樹狀數組,實際上是一種可以支持區間修改和區間查詢的樹狀數組,和線段樹相比,它的常數極小,不須要太多空間,代碼量也少了不少(簡直吊打線段樹)ui
1.樹狀數組spa
既然是超級樹狀數組,那麼就須要一個樹狀數組做爲基礎了。可是在真正實現時,只用到了lowbit()函數(因此說lowbit是樹狀數組的核心啊)調試
2.準備工做code
首先,咱們須要一個差分數組。blog
設a[]數組爲原數組,那麼tree[](差分數組)定義爲tree[i]=a[i]-a[i-1]get
猴子也能一眼看出的性質:a[i]=tree[1]+tree[2]+tree[3]+...+tree[i]數學
3.區間查詢it
(爲何先說查詢呢。。)
(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線段樹你真的能夠當場去世了~