在網上下到acm大牛總結的線段樹專輯,其代碼風格我學到了很多。
線段樹,類似區間樹,是完全二叉樹,它在各個節點保存一條線段(數組中的一段子數組),主要用於高效解決連續區間的動態查詢問題,由於二叉結構的特性,它基本能保持每個操作的複雜度爲O(lgN)!
性質:父親的區間是[a,b],用二分法的思想去分左右兒子,左兒子爲[a,(a+b)/2],右兒子爲[(a+b)/2+1,b],因此線段樹所需空間爲最大數的4倍
首先宏定義左右兒子:
#define lson l,m,rt<<1<span> //結點爲rt,區間爲[l,m]</span>
#define rson m+1,r,rt<<1|1 <span> //結點爲rt,區間爲[m+1,r]</span>
計算父節點的總值,總值爲左右兒子值的總和:
<span style="font-family:FangSong_GB2312;">inline void fatherSum(int rt){
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}</span>
用遞歸思想構造線段樹:
<span style="font-family:FangSong_GB2312;">void buildTree(int l,int r,int rt){
if(l == r){
scanf("%d",&sum[rt]);
return;
}
int m = (l+r)>>1;
buildTree(lson);
buildTree(rson);
fatherSum(rt);
} </span>
線段樹對應結點更新以及父節點更新以及父節點的父節點的更新...,樹根遍歷到對應結點所經過的結點的值都要更新:
<span style="font-family:FangSong_GB2312;">void update(int p,int num,int l,int r,int rt){
if(l == r){
sum[rt] += num;
return;
}
int m = (l+r)>>1;
if(p <= m)
update(p,num,lson);
else
update(p,num,rson);
fatherSum(rt);
}</span>
區間查詢:
<span style="font-family:FangSong_GB2312;">int query(int L,int R,int l,int r,int rt){
if(L <= l && r <= R)
return sum[rt];
int m = (l+r)>>1;
int ret = 0;
if(L <= m)
ret += query(L,R,lson);
if(R > m)
ret += query(L,R,rson);
return ret;
}</span>