生活随笔
收集整理的這篇文章主要介紹了
树状数组学习小结
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
樹狀數組,又稱二進制索引樹,英文名Binary Indexed Tree。
一、樹狀數組的用途
主要用來求解數列的前綴和,a[0]+a[1]+...+a[n]。
由此引申出三類比較常見問題:
1、單點更新,區間求值。(HDU1166)
2、區間更新,單點求值。(HDU1556)
3、求逆序對。(HDU2838)
?
二、樹狀數組的表示
1、公式表示
設A[]為一個已知的數列。C[]為樹狀數組。則會有
C[i]=A[j]+...+A[i];j=i&(-i)=i&(i^(i-1))。
2、圖形表示
(注:1、最下面的一行表示數組A,上面的二進制表示的部分是C;
2、圖片來源于http://hi.baidu.com/rain_bow_joy/blog/item/569ec380c39730d2bc3e1eae.html)
?
從以上可以發現:
1、樹狀數組C是表示普通數組A的一部分的和。
2、小標為奇數時,C[i]只能管轄一個A[i]。
3、C[i]的最后一個數一定是A[i]。
?
三、樹狀數組的關鍵代碼
1、
[cpp]?view plaincopyprint?
int?lowBit(int?x)?? {?? ????return?x&(-x);?? }??
這段代碼可以簡單的理解為是樹狀數組向前或向后衍生是用的。
向后主要是為了找到目前節點的父節點,比如要將C[4]+1,那么4+(4&(-4))=8,C[8]+1,8+(8&(-8))=16,
C[16]+1。
向前主要是為了求前綴和,比如要求A[1]+...+A[12]。那么,C[12]=A[9]+...+A[12];然后12-12&(-12)=8,
C[8]=A[1]+...+A[8]。
?
2、
[cpp]?view plaincopyprint?
void?modify(int?pos,int?num)???? {?? ????while(pos<=n)????? ????{?? ????????c[pos]+=num;?? ????????pos+=lowBit(pos);?? ????}?? }??
這段代碼是用來更新樹狀數組的,包括區間更新、單點更新。
就是想剛才所說的,一點更新了,要不斷將父節點也更新。
?
3、
[cpp]?view plaincopyprint?
int?getResult(int?pos)???? {?? ????int?sum=0;??? ????while(pos>0)?? ????{?? ????????sum+=c[pos];?? ????????pos-=lowBit(pos);?? ????}?? ?????? ????return?sum;??? }??????????????????
這段代碼用來求解前綴和的。
就像剛才說的,求解A[1]+...+A[12],也就是C[12]+C[8]搞定。
?
四、樹狀數組的優點
1、原本的長度為n的數列求和時間復雜度為O(n),更改的時間復雜度為O(1)。
樹狀數組將其優化為O(logn)。在n較大時,效率更高。
2、樹狀數組編碼簡單。
?
五、注意
1、樹狀數組的下標要從1開始。
2、在學習的過程中遇到這么個問題。不知道為什么pos+pos&(-pos)就到了pos的父節點,也不知道
為什么pos-pos&(-pos)就得到了下一個無聯系的節點,從而可以得到前綴和。
我只能說:我不懂如何證明,這是數學問題了,樹狀數組的發明者應該就是發現了這點才搞出樹狀
數組的吧。初學者不妨拋開這點,專注于事實,將上面的圖形自己計算畫一遍,非常有利于理解。
?
六、符代碼:
HDU1166
單點更新,區間求值
[cpp]?view plaincopyprint?
#include<iostream>?? using?namespace?std;?? ?? const?int?maxn=50001;?? ?? int?a[maxn];?? int?c[maxn];?? int?n;??? ?? int?lowBit(int?t)?? {?? ????return?t&(-t);?? }?? ?? void?modify(int?t,int?num)?? {?? ????while(t<=n)?? ????{?? ????????c[t]+=num;?? ????????t+=lowBit(t);?? ????}?? }?? ?? int?getResult(int?t)?? {?? ????int?num=0;??? ????while(t>0)?? ????{?? ????????num+=c[t];?? ????????t-=lowBit(t);?? ????}?? ?????? ????return?num;??? }?? ?? void?init()?? {?? ????for(int?i=1;i<=n;i++)?? ????{?? ????????scanf("%d",&a[i]);?? ?????????? ????????modify(i,a[i]);??? ????}?? }?? ?? ?? int?main()?? {?? ????int?cas,Case=1;?? ?????? ????scanf("%d",&cas);??? ????while(cas--)?? ????{??? ????????memset(c,0,sizeof(c));??? ????????printf("Case?%d:\n",Case++);?? ??????????? ????????scanf("%d",&n);?? ?????????? ????????init();?? ?????????? ????????char?ch[15];?? ????????int?a,b;????? ????????while(scanf("%s",&ch),strcmp(ch,"End"))?? ????????{??? ????????????scanf("%d%d",&a,&b);?? ??????????????? ????????????switch(ch[0])?? ????????????{?? ????????????????case?'Q':?? ????????????????????printf("%d\n",getResult(b)-getResult(a-1));?? ????????????????????break;??? ????????????????case?'A':??? ????????????????????modify(a,b);?? ????????????????????break;?? ????????????????case?'S':?? ????????????????????modify(a,-b);?? ????????????????????break;?? ????????????}?? ?????????}?? ?????}?? ??????? ?????system("pause");?? ?????return?0;?? }???
?
HDU1556
區間更新,單點求值
[cpp]?view plaincopyprint?
#include<iostream>?? #include<cstring>?? using?namespace?std;?? ?? const?int?maxn=100001;?? ?? int?c[maxn];?? int?n;?? ?? int?lowbit(int?t)?? {?? ????return?t&(-t);?? }?? ?? void?insert(int?t,int?d)?? {?? ????while(t<=n)?? ????{?? ????????c[t]+=d;?? ????????t+=lowbit(t);?? ????}?? }?? ?? int?getSum(int?t)?? {?? ????int?sum=0;?? ????while(t>0)?? ????{?? ????????sum+=c[t];?? ????????t-=lowbit(t);?? ????}?? ?????? ????return?sum;?? }?? ?? int?main()?? {?? ????while(cin>>n,n)?? ????{?? ????????int?a,b;?? ????????memset(c,0,sizeof(c));?? ?????????? ????????for(int?i=1;i<=n;i++)?? ????????{?? ????????????scanf("%d%d",&a,&b);?? ?????????????? ????????????insert(a,1);?? ????????????insert(b+1,-1);?? ????????}?? ?????????? ???????for(int?j=1;j<n;j++)?? ???????{?? ????????????printf("%d?",getSum(j));?? ???????}?? ???????printf("%d\n",getSum(n));?? ????}?? ?????? ????system("pause");?? ????return?0;?? }??
?
HDU2838
求逆序對
[cpp]?view plaincopyprint?
#include<iostream>?? #include<cstring>?? using?namespace?std;?? ?? const?int?maxn=100001;?? ??? struct?node?? {?? ????int?cnt;?? ????__int64?sum;?? }tree[maxn];??????????? ?????????? int?n;?? ?? int?lowBit(int?x)?? {?? ????return?x&(-x);?? }?? ?? void?modify(int?x,int?y,int?t)?? {?? ????while(x<=n)?? ????{?? ????????tree[x].sum+=y;?? ????????tree[x].cnt+=t;???? ????????x+=lowBit(x);?? ????}?? }?? ?? __int64?query_cnt(int?x)????? {?? ????__int64?sum=0;?? ????while(x>0)?? ????{?? ????????sum+=tree[x].cnt;?? ????????x-=lowBit(x);?? ????}?? ?????? ????return?sum;?? }?? ?? __int64?query_sum(int?x)???? {?? ????__int64?sum=0;?? ????while(x>0)?? ????{?? ????????sum+=tree[x].sum;?? ????????x-=lowBit(x);?? ????}?? ?????? ????return?sum;?? }?? ?? int?main()?? {?? ????while(~scanf("%d",&n))?? ????{?? ????????int?a;?? ????????__int64?ans=0;??? ????????memset(tree,0,sizeof(tree));?? ??????????? ????????for(int?i=1;i<=n;i++)?? ????????{?? ????????????scanf("%d",&a);?? ?????????????? ????????????modify(a,a,1);???? ?????????????? ????????????__int64?k1=i-query_cnt(a);????? ????????????if(k1!=0)?? ????????????{?? ????????????????__int64?k2=query_sum(n)-query_sum(a);??? ????????????????ans+=k1*a+k2;????? ????????????}?? ????????}?? ?????????? ????????printf("%I64d\n",ans);??? ????}?? ?????? ????system("pause");?? ????return?0;?? }???
?
七、二維樹狀數組
C[x][y]=sum(A[i][j])。其中,x-lowBit(x)+1<=i<=x,y-lowBit(y)+1<=j<=y。
例題:HDU1892
二維樹狀數組一般就是對矩陣的操作,更新、求值。。。
代碼:
[cpp]?view plaincopyprint?
#include<iostream>?? #include<cstring>?? using?namespace?std;?? ?? const?int?maxn=1005;?? ?? int?c[maxn][maxn];?? ?? int?lowBit(int?x)?? {?? ????return?x&(-x);?? }?? ?? void?modify(int?x,int?y,int?val)?? {?? ????for(int?i=x;i<maxn;i+=lowBit(i))?? ????{?? ????????for(int?j=y;j<maxn;j+=lowBit(j))?? ????????{?? ????????????c[i][j]+=val;?? ????????}?? ????}?? }?? ?? int?getResult(int?x,int?y)?? {?? ????int?sum=0;?? ????for(int?i=x;i>0;i-=lowBit(i))?? ????{?? ????????for(int?j=y;j>0;j-=lowBit(j))?? ????????{?? ????????????sum+=c[i][j];?? ????????}?? ????}?? ?????? ????return?sum;?? }?? ?? int?getVal(int?x,int?y)?? {?? ????return?getResult(x,y)-getResult(x-1,y)-getResult(x,y-1)+getResult(x-1,y-1);?? }?? ?? void?init()?? {?? ????memset(c,0,sizeof(c));?? ?????? ????for(int?i=1;i<maxn;i++)?? ????{?? ????????for(int?j=1;j<maxn;j++)?? ????????{?? ????????????modify(i,j,1);?? ????????}?? ????}?? }?? ?????? int?main()?? {?? ????int?cas,cas1=1,query;?? ?????? ????scanf("%d",&cas);?? ????while(cas--)?? ????{?? ????????init();?? ?????????? ????????scanf("%d",&query);?? ?? ????????printf("Case?%d:\n",cas1++);?? ????????for(int?i=1;i<=query;i++)?? ????????{?? ????????????char?ch;?? ????????????int?x1,y1,x2,y2,n;?? ?????????????? ????????????getchar();?? ????????????scanf("%c",&ch);?? ?????????????? ????????????switch(ch)?? ????????????{?? ????????????????case?'S':?? ????????????????????{?? ????????????????????scanf("%d%d%d%d",&x1,&y1,&x2,&y2);?? ????????????????????int?x11=min(x1,x2);?? ????????????????????int?x22=max(x1,x2);?? ????????????????????int?y11=min(y1,y2);?? ????????????????????int?y22=max(y1,y2);?? ????????????????????printf("%d\n",getResult(x22+1,y22+1)-getResult(x11,y22+1)-getResult(x22+1,y11)+getResult(x11,y11));?? ????????????????????break;?? ????????????????????}?? ????????????????case?'A':?? ????????????????????{?? ????????????????????scanf("%d%d%d",&x1,&y1,&n);?? ????????????????????modify(x1+1,y1+1,n);?? ????????????????????break;?? ????????????????????}?? ????????????????case?'M':?? ????????????????????{?? ????????????????????scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&n);?? ????????????????????int?v=getVal(x1+1,y1+1);?? ????????????????????int?Min=min(n,v);?? ????????????????????modify(x1+1,y1+1,-Min);?? ????????????????????modify(x2+1,y2+1,Min);?? ????????????????????break;?? ????????????????????}?? ????????????????case?'D':?? ????????????????????{?? ????????????????????scanf("%d%d%d",&x1,&y1,&n);?? ????????????????????int?v=getVal(x1+1,y1+1);?? ????????????????????int?Min=min(v,n);?? ????????????????????modify(x1+1,y1+1,-Min);?? ????????????????????break;?? ????????????????????}?? ????????????}?? ????????}?? ????}?? ?????? ????system("pause");?? ????return?0;?? }??
?
八、參考文章
http://mindlee.net/2011/07/10/binary-indexed-trees/?
http://dongxicheng.org/structure/binary_indexed_tree/
總結
以上是生活随笔為你收集整理的树状数组学习小结的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。