在一些計數問題中,線段樹用於維護值域(一段權值范圍),這樣的線段樹也稱為權值線段樹。為了降低空間復雜度,我們可以不建出整棵線段樹的結構,而是在最初只建立一個根節點,代表整個區間,當需要訪問線段樹的某棵子樹(某個子區間)時,再建立代表這個子區間的節點。采用這種方法維護的線段樹稱為動態開點的線段樹。動態開點的線段樹拋棄了完全二叉樹父節點的2倍編號規則,改為使用變量記錄左右子節點的編號(相當於指針)。同時,它也不再保存每個節點代表的區間,而是在每次遞歸訪問的過程中作為參數傳遞。下面是一個動態開點的線段樹的節點結構。
struct segment_tree{
int lc,rc; //左右子節點的編號
int dat; //區間最大值
}tree[maxn<<2];
int root,tot;
inline int build(){ //新建一個節點
tot++;
tree[tot].lc = tree[tot].rc = tree[tot].dat = 0;
}
int main(){
tot = 0;
root = build(); //根節點
}
下面的代碼對線段樹單點修改的過程稍加變動,實現了在動態開點的線段樹中把val位置上的值加delta,同時維護區間最大值的操作
inline void update(int p,int l,int r,int val,int delta){
if(l == r){
tree[p].dat += delta;
return;
}
int mid = (l+r)>>1; //代表的區間[l,r] 作為遞歸參數傳遞
if(val <= mid){
if(!tree[p].lc)tree[p].lc = build();
update(tree[p].lc,l,mid,val,delta);
}
else{
if(!tree[p].rc)tree[p].rc = build(); //動態開點
update(tree[p].rc,mid+1,r,val,delta);
}
tree[p].dat = max(tree[tree[p].lc].dat,tree[tree[p].rc].dat);
}
