一、線段樹的概論
二、線段樹的性質
三、線段樹的建樹
四、線段樹的單點修改
五、線段樹的區間查詢
六、線段樹的區間修改
七、**實現
假設有編號從1到n的n個點,每個點都存了一些資訊,用[l,r]表示下標從l到r的這些點。
線段樹的用處就是,對編號連續的一些點進行修改或者統計操作,修改和統計的複雜度都是o(log2(n)).
線段樹的原理,就是,將[1,n]分解成若干特定的子區間,然後,將每個區間[l,r]都分解為少量特定的子區間,通過對這些少量子區間的修改或者統計,來實現快速對[l,r]的修改或者統計。
由此看出,用線段樹統計的東西,必須符合區間加法,否則不可能通過分成的子區間來得到[l,r]的統計結果。
符合區間加法的例子:
數字之和——總數字之和 = 左區間數字之和 + 右區間數字之和
最大值——總最大值=max(左區間最大值,右區間最大值)
不符合區間加法的例子:
眾數——只知道左右區間的眾數,沒法求總區間的眾數
如果觀察一下線段樹(無奈這裡木有圖),會發現除去線段樹的最後一層,整顆線段樹是一顆完全二叉樹,樹深為o(logn),因此我們可以用「父子二倍」標號方法:
1.根節點編號為1
2.編號為x的節點的左子節點編號為x*2,右子節點編號為x*2+1
線段樹的基本用途是對序列進行維護,支援查詢與修改指令。給定乙個長度為n的序列a,我麼可以在序列[1,n]上建立一顆線段樹,每個葉節點[i,]儲存a[i]的值。線段樹的二叉樹結構可以很方便地從下往上傳遞資訊。以區間最大值為例,d(l,r)等於max,顯然d(l,mid)=max(d(l,mid),d(mid+1,r))
下面的**建立了線段樹並保留了最大值:
單點修改是一條形如「c x v"的指令,表示把a[x]的值修改為v
時間複雜度為o(logn)
區間查詢是一條形如」q l r"的指令,例如查詢序列a在區間[l,r]上的最大值,即max.我們只需要從根節點開始
1.若「l,r"完全覆蓋了當前節點代表的空間,則立即回溯,並且將該節點的值為候選答案
2.若左子節點與[l,r]有重疊部分,則遞迴左子節點
3.若右子節點與[l,r]有重疊部分,則遞迴右子節點
線段樹的區間修改也是將區間分成子區間,但是要加乙個標記,稱作懶惰標記(lazy_tag)。
標記的含義:本節點的統計資訊已經根據標記更新過了,但是本節點的子節點仍需要進行更新。
即,如果要給乙個區間的所有值都加上k,那麼,實際上並沒有給這個區間的所有值都加上k,而是打個標記,記下來,這個節點所包含的區間需要加k.打上標記後,要根據標記更新本節點的統計資訊,比如,如果本節點維護的是區間和,而本節點包含5個數,那麼,打上+k的標記之後,要給本節點維護的和+5k。
(1)定義
#define n 100005
int a[n],n,n;//原陣列
int sum[n<<2];
int add[n<<2];//懶惰標記
(2)建樹
void pushup(int rt)
void build(int l,int r,int rt)
int m=(l+r)>>1;
//左右遞迴
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
//更新
pushup(rt);
}
(3)點修改
void update(int l,int c,int l,int r,int rt)
int m=(l+r)>>1;
//根據條件判斷往左子樹呼叫還是往右
if(l <= m) update(l,c,l,m,rt<<1);
else update(l,c,m+1,r,rt<<1|1);
pushup(rt);//子節點更新了,本節點也需要更新資訊
}
(4)下推標記
void pushdown(int rt,int ln,int rn)
}
(5)區間修改
void update(int l,int r,int c,int l,int r,int rt)
int m=(l+r)>>1;
pushdown(rt,m-l+1,r-m);//下推標記
//判斷左右子樹跟[l,r]有無交集
if(l <= m) update(l,r,c,l,m,rt<<1);
if(r > m) update(l,r,c,m+1,r,rt<<1|1);
pushup(rt);//更新
}
(6)區間查詢
int query(int l,int r,int l,int r,int rt)
int m=(l+r)>>1;
//下推標記
pushdown(rt,m-l+1,r-m);
int ans=0;
if(l <= m) ans+=query(l,r,l,m,rt<<1);
if(r > m) ans+=query(l,r,m+1,r,rt<<1|1);
return ans;
}
(7)函式呼叫
//建樹
build(1,n,1);
//點修改
update(l,c,1,n,1);
//區間修改
update(l,r,c,1,n,1);
//區間查詢
int ans=query(l,r,1,n,1);
線段樹總結
線段樹總結 線段樹的原理就是每乙個區間都可以被分成若干個不相交連續區間 重要 線段樹維護的資料 1.自身結構的資料 比如 左兒子 右兒子的編號 2.懶惰標記 整段區間都變成乙個值 或者將要進行什麼操作 根據每次操作的型別 把操作的區間分成若干個不連續的區間 然後把操作的標記賦值給相應的區間 3.答案...
線段樹總結
線段樹的入門級 總結 線段樹是一種二叉搜尋樹,與區間樹相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。對於線段樹中的每乙個非葉子節點 a,b 它的左兒子表示的區間為 a,a b 2 右兒子表示的區間為 a b 2 1,b 因此線段樹是平衡二叉樹,最後的子節點數目為n,即整...
線段樹總結
解決的題目 對區間所對應的一些資料進行修改,查詢。基本步驟 先建樹,然後插入資料,然後更新,查詢。關鍵部分 用線段樹解題,關鍵是要想清楚每個節點要存哪些資訊以及這些資訊如何高效更新,維護,查詢。不要一更新就更新到葉子節點,那樣更新效率最壞就可能變成o n 的了。建樹的方式 1 陣列 若根節點下標為0...