為了降低權值線段樹的空間複雜度,可以不直接建出整棵線段樹的結構,而是在最初只建立乙個根節點,當需要訪問某棵為建立的子樹的時候,再建立代表這個子樹的節點。
動態開點的線段樹用變數記錄左右節點的編號。
值域為1-n的動態開點線段樹在m次單點修改後,節點規模為o(mlogn)
例題:p1908 逆序對
(這題n最大1e9,正常應該用離散化做,但是有了動態開點不怕)
涉及單點修改、查詢區間和
code:
#include
#include
#include
#include
#include
typedef
long
long ll;
using
namespace std;
const
int maxm=
1e7+5;
struct nodetr[maxm]
;int cnt;
intbuild()
void
init()
void
pushup
(int node)
void
update
(int x,
int v,
int l,
int r,
int node)
int mid=
(l+r)/2
;if(x<=mid)
else
pushup
(node);}
intask
(int st,
int ed,
int l,
int r,
int node)
int mid=
(l+r)/2
;int ans=0;
if(st<=mid)
if(ed>=mid)
return ans;
}int
main()
printf
("%lld"
,ans)
;return0;
}
若多棵動態開點線段樹值域都為1-n,顯然所有樹對於子區間的劃分是相同的。
現在要把所有樹的資訊整合在一起,即線段樹合併.
合併流程:
選擇兩棵線段樹,用p,q指向兩棵樹的根節點,向下遞迴合併,(遞迴方向必須相同,即p,q代表相同區間)。
如果過程中p,q某一為空,則以非空的作為合併之後的節點。
否則繼續遞迴合併兩棵左子樹和兩棵右子樹,回溯時刪除其中乙個節點(如刪除q),以另一節點作為合併之後的節點(如p),同時自底向上統計合併後的資訊。
code:
維護區間最值
const
int maxm=
1e7+5;
struct nodetr[maxm]
;int
merged
(int p,
int q,
int l,
int r)
因為發生遞迴必有一節點被刪除,因此函式執行次數不會超過節點總數+1,因此複雜度為o(mlogn),與單點操作複雜度相同 線段樹 動態開點
在一些計數問題中,線段樹用於維護值域 一段權值範圍 這樣的線段樹也稱為權值線段樹。為了降低空間複雜度,我們可以不建出整棵線段樹的結構,而是在最初只建立乙個根節點,代表整個區間,當需要訪問線段樹的某棵子樹 某個子區間 時,再建立代表這個子區間的節點。採用這種方法維護的線段樹稱為動態開點的線段樹。動態開...
動態開點線段樹
前置芝士 眾所周知,普通線段樹空間複雜度是 o n 4 所以當n很大的時候,如果正常的去建一顆線段樹,開4倍n空間顯然會炸記憶體 怎麼辦呢?這個時候,動態開點線段樹出現了。概念 動態開點線段樹是一類特殊的線段樹,與普通的線段樹不同的是,每乙個節點的左右兒子不是該點編號的兩倍和兩倍加一,而是現加出來的...
權值線段樹 動態開點
普通平衡樹 題目給的資料是1e 7到1e7,直接寫線段樹記憶體肯定是比較吃力,而且題目還要維護rank和第k大,這時候就用到動態開點了,因為運算元一共就1e5,所以最多也只需要開 log 2 2e7 大小的陣列。修改函式void add int rt,int l,int r,int x,int v ...