樹狀數組小結


 

樹狀數組小結

轉載自: http://blog.csdn.net/shahdza/article/details/6314818

又做了幾道樹狀數組的題,決定放一塊兒總結一下;恩,總結一下。。

(ps:大牛可以直接跳過。。。)

這得從一張圖說起;




樹狀數組中用的d【】,每個點都有一定的管轄范圍;

如d[1]=a[1];

d[2]=a[1]+a[2];

d[3]=a[3];

d[4]=a[1]+a[2]+a[3]+a[4];

等等;

這樣的結構關鍵是為了,對一個數組內部動態的刪除,增加,來高效的求某個點或者某個區間的值;

比如說對數組a,改變某一位的值需O(1),求某個k區間值O(k);

這樣的m次操作是用O(m*k);

顯然數據很大時,效率要差很多;

而樹狀數組,它改變某一位,或者求某個區間的和,都是O(logN);效率大為改善;

對於上圖怎么計算區間的和;關鍵是需要幾個函數;

先說樹狀數組最簡單的用法,修改上面圖中的某個點,並求某段區間的和;

第一個函數;

1 int lowbit(int x)
2 {
3     return x&(-x);
4 }

 


這個函數主要是用來求的是某個點管轄范圍;

x&(-x);這個東西的由來就不說了。。。灰常奇妙啊。。

如果是x+=x&(-x);就是得到的改點的父節點的值;比如x=4時;就能得到8;

而x-=x&(-x)的話,就是得到x這個點的管轄區間的下個區間的管轄點;

比如說,x=7,代入后6;不斷循環到0能依次得到 6.。。4.;

把他們所有的管轄區間正好是1....7;

第二個函數

1 void update(int x,int num)
2 {
3     while(x<=N)
4      {
5          d[x]+=num;
6          x+=lowbit(x);
7      }
8 }

 

這個函數,是用來修改樹狀數組的;

如果是一般的算法只用修改改點就可;但是樹狀數組必須修改所有改點被管轄的區間;

比如把a數組的 a[2]減去1,(令N=16);則所有2被管轄的點有4,8,16都應該減去1;

就是調用函數 update(2,-1);
第三個函數

 1 int getSum(int x)
 2 {
 3     int s=0;
 4     while(x>0)
 5      {
 6          s+=d[x];
 7          x-=lowbit(x);
 8      }
 9     return s;
10 }

 

這個函數就是求區間和了。。比如getSum(7)的話,就是求a[1]+a[2]+...a[7];

上面是最基本的用法;要學習樹狀數組必須把上面的過程原理搞明白;

搞明白d數組和原來a數組的區別,初學者應該自己畫一畫;

另外有好幾種最原始樹狀數組的變形(一會兒討論二維的);

大體上可以分為兩種;

一,每次修改的是一個點,所求的是關於某段區間;

這種情況最好辦;比如說poj2352 stars;求每個點前面比他小的點的個數;

只用設置數組a[],先全是0,然后有某個點就依次修改,並以此統計;

這一種是最基本的向上修改,向下統計;(基本上都是。。。)

二,每次修改的是一個區間,所求的值是關於某個點的;

代表的典型題目是HOJ1556 color the ball;

這個題是每次修改了一整個區間,最后求的是每個點修改的次數;

這個需要將上面的函數,稍加修改;

1 void update(int x,int num)
2 {
3     while(x>0)
4      {
5          d[x]+=num;
6          x-=lowbit(x);
7      }
8 }

 

要向下修改,將它后面的區間都加一遍;

再向后修改,把不必要的修改區間再減去;

用他的父節點記錄每個點的染色次數;

統計時要

 1 int getSum(int x)
 2 {
 3     int s=0;
 4     while(x<=N)
 5      {
 6          s+=d[x];
 7          x+=lowbit(x);
 8      }
 9     return s;
10 }

 

這種修改一個區間,而求某個點的修改次數的,一般的都是向下修改,向上統計;

對於二維的情況,d[][]中的d[i][j]這個點也有他自己的管轄區間,從一維推廣到二維;可以很容易理解;

一維情況是一段區間的管轄范圍,二維就是一個矩形的管轄范圍,而每一維可以獨立考慮;

*****************************************************************************

下面是幾道題目的簡單分析;

一,兩種情況;
1,要向上統計,向下修改;一般是修改一段區間的值,查找的是某個位上的值;

HDU1556 color the ball;

這個題是這類題最基本的;關鍵是理解這個向上統計,向下修改時怎么操作的和原理;

poj2155 Matrix

這個是樓天成出的題目;是一道很典型的二維數狀數組的應用;是hdu1556的二維版本;

記住要向上統計,向下修改;二維情況,最主要的是理解那個數組中的每個點保存的值的意義(一個矩形區域的總和);最后記住取余;

poj2299 Ultra-QuickSort;(求逆序數);

這個題可以用歸並排序做出,這里就略過不說了。。

求逆序數,也是樹狀數組的典型應用,求一堆數中前面比他大的數的個數;也需要向上統計,向下修改;

可以與求一串數中前面比他小的數的個數這個題來比較思考。。。

poj3067 Japan

此題要先對x排序,再對y求那個前面比他大的數的個數;(理解下);

也是基本應用;

**************************************************************************************************************************

二,2,要向上修改,向下統計;一般是修改某個位置上的值,查找的是一段區間的和;

poj2352 stars

這個題是最基本的一維情況,剛才已說過。。

poj1195 Mobile phone

是個二維的情況。改變的是某個位置上的數的大小;就將所有管轄這個點的 點修改;

統計的時候,就是向下統計;

poj2481 Cows

這個題要把y按不降排序,x不升排序,基本是就是poj2352了。。不過注意其中可以有完全重合區間;

poj3321 apple Tree;

這題要用到樹狀數組;但難度在那個dfs怎么求那個時間戳;求出后,在套入樹狀數組的那部分東東。就搞定了。。

poj1990 MooFest ;

這題O(n^2)算法必然超時;排序后要存儲前面比他小的數的總和,和個數;故樹狀數組;

poj2309

樹狀數組的小試牛刀,一下搞定,想明白的話。。

可以用樹狀數組的地方,一定可以用線段樹;反過來則不行;

但是,樹狀數組編碼簡單,對於一定區間修改,求值,很高效;

另外,一個講樹狀數組灰常好的網址要吐血推薦。。。

http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=binaryIndexedTrees#prob

 

 

================================================================================================

 

 

樹狀數組,剛開始學習的時候覺得很神奇,特別是求lowbit的時候...寫過幾題之后,覺得這東西還是挺好用的。需要注意一個超時陷阱:x==0的情況

樹狀數組在動態求區間之和的問題時候是一個很好的選擇。給出幾個例題:

(1):http://acm.pku.edu.cn/JudgeOnline/problem?id=2352 starts

題目大意為在二維坐標上給出一些星星的坐標,求某一個星星左方,下方,左下方的星星個數。求解這題時學習到了 “降維” 的思想,首先把星星按照Y坐標從小到大,X從小到大排序。樹狀數組以X坐標建立。這樣,在每次對一個星星進行統計時,之前出現過的星星,只要X坐標比其小,則必在其左,下,左下方。 這時,只需利用樹狀數組進行求從1~x的和既可,統計完之后要記得更新樹狀數組(后面附上代碼) (注意坐標為0的情況,這里是一個超時陷阱)

(2):http://acm.pku.edu.cn/JudgeOnline/problem?id=2155 Matrix

二維樹狀數組,題目大意為一個二維矩形平面,里面有數字0或1,初始時全為0,給出兩種操作,一種是改變一段子區間內的數值,把0改為1或者1改成0。另外一種是查詢某一坐標上的數值是0還是1,。

數據范圍:坐標N<=1000,操作數T <=50000。

解此題的方法很巧妙,首先我們考慮如果改變區間內數值時,把該區間內的每一個點都改變一次,那么時間一次操作就需要n^2,這樣太耗時。想象一下,如果我們在求某一點的操作次數時,把求操作次數轉化為求該點到(1,1)這點的和,那么我們在對某區間操作時,我們可設立四個哨兵來標記:

如圖,當我們要改變區間(3,3)~(6,5)的數值時,分別在按照上圖所示改變矩陣的數值,這時,區間內部到(1,1)點的和為1,而區間外部到(1,1)點的和為0,這樣就吧問題轉換為了動態求二維區間和的問題
,這時就用二維樹狀數組來解決既可

(3):http://acm.pku.edu.cn/JudgeOnline/problem?id=3321

題目大意為給出一棵樹,節點數<=100000,樹中的節點可能有蘋果,兩種操作:一種是改變某一節點的蘋果,有改為無,無改為有。第二種操作是求某一節點和以該節點為根的子樹的蘋果總數。操作數達到了100000;

這題需要把一個樹形結構變換為線性結構,然后使用樹狀數組動態求和。變換方法為DFS一次這棵樹,記錄下每個節點第一次訪問和最后一次訪問時的順序編號,這時,在某節點第一次訪問和最后一次訪問編號之間的訪問編號,必定為該節點的子節點,按照訪問編號建立樹狀數組,當改變某一節點蘋果時,只需按照該節點第一次訪問的編號在樹狀數組中修改值,查詢時統計某節點在第一次訪問和最后一次訪問編號之間的和。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM