線段樹動態開點

2021-09-26 13:50:16 字數 1608 閱讀 9321

為了降低權值線段樹的空間複雜度,可以不直接建出整棵線段樹的結構,而是在最初只建立乙個根節點,當需要訪問某棵為建立的子樹的時候,再建立代表這個子樹的節點。

動態開點的線段樹用變數記錄左右節點的編號。

值域為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 ...