主席樹學習筆記

2022-07-02 19:00:17 字數 3854 閱讀 9460

這篇文章是我對學***樹的乙個小結。

當然,筆者很菜,除了模板就什麼都不會打,也會有一些知識理解得不夠深刻。

因此,這篇文章以後還會不定期更新的。

主席樹,全名稱為可持久化線段樹,也就是把每個階段的線段樹儲存起來,每次可以直接訪問乙個階段的狀態,是神犇\(hjt\)發明出來的一種資料結構。

先放出乙個經典模板題:靜態區間第\(k\)小

如題,給定\(n\)個整數構成的序列,將對於指定的閉區間查詢其區間內的第k小值。

輸入格式

第一行包含兩個正整數\(n\)、\(m\),分別表示序列的長度和查詢的個數。

第二行包含\(n\)個整數,表示這個序列各項的數字。

接下來\(m\)行每行包含三個整數\(l, r, k\) , 表示查詢區間\([l, r]\)內的第\(k\)小值。

輸出格式

輸出包含\(k\)行,每行\(1\)個整數,依次表示每一次查詢的結果

輸入樣例#1

5 5

25957 6405 15770 26287 26465

2 2 1

3 4 1

4 5 1

1 2 2

4 4 1

輸出樣例#1
6405

15770

26287

25957

26287

資料範圍

對於\(20\%\)的資料滿足:\(1 \leq n, m \leq 10\)

對於\(50\%\)的資料滿足:\(1 \leq n, m \leq 10^3\)

對於\(80\%\)的資料滿足:\(1 \leq n, m \leq 10^5\)

對於\(100\%\)的資料滿足:\(1 \leq n, m \leq 2\cdot 10^5\)

對於數列中的所有數\(a_i\)​,均滿足\(-^9 \leq a_i \leq ^9\)

樣例資料說明

\(n=5\),數列長度為\(5\),數列從第一項開始依次為\([25957, 6405, 15770, 26287, 26465 ]\)

第一次查詢為\([2, 2]\)區間內的第一小值,即為\(6405\)

第二次查詢為\([3, 4]\)區間內的第一小值,即為\(15770\)

第三次查詢為\([4, 5]\)區間內的第一小值,即為\(26287\)

第四次查詢為\([1, 2]\)區間內的第二小值,即為\(25957\)

第五次查詢為\([4, 4]\)區間內的第一小值,即為\(26287\)

經典的主席樹題。

由於本題中的\(a_i\)資料範圍很大,因此我們需要將資料進行離散化處理。

然後就開始執行主席樹的操作。

先來看看插入操作的**:

void add(int &num, int &x, int l, int r)

和線段樹很相似,不是嗎?(主席樹不就是可持久化的線段樹嗎)

接著看尋找答案的**:

int getans(int i, int j, int k, int l, int r)

和線段樹的思想一樣,分開去找左右兩子樹。

**就這麼一點兒,並不難記。

不難得出最後的**:

#include #include #include #include #include #include #include #include #include #include #include using namespace std;

inline int gi()

while (c >= '0' && c <= '9')

return f * x;

}struct node

t[4000003];

struct lisan

} a[4000003];

int r[2000003], t[2000003], n, m, cnt;

void add(int &num, int &x, int l, int r)

int getans(int i, int j, int k, int l, int r)

int main()

sort(a + 1, a + n + 1);//離散化

for (int i = 1; i <= n; i++) r[a[i].idx] = i;

cnt = 1;

for (int i = 1; i <= n; i++)

while (m--)

return 0;

}

可持久化陣列屬於主席樹的一種變形,它是帶修改的。

即:可持久化陣列是一種支援單點修改、單點查詢的可持久化資料結構。

我怎麼感覺可持久化陣列比主席樹還簡單啊

還是來邊看**邊理解思路:

定義乙個結構體,儲存每個節點的資訊:左兒子、右兒子、權值。

struct node

q[20000005];

新建乙個節點:

inline int newnode(int x)

來看建樹的**(其實就是新建節點的過程):

int jianshu(int node, int beg, int en)

int mid = (beg + en) >> 1;

q[node].l = jianshu(q[node].l, beg, mid);//左兒子

q[node].r = jianshu(q[node].r, mid + 1, en);//右兒子

return node;

}

更新數值:

int modify(int node, int beg, int en, int x, int y)

return node;

}

尋找答案:

int getans(int node, int beg, int en, int x)

}

不難得出全部**:

#include #include #include #include #include #include #include using namespace std;

inline int gi()

while (c >= '0' && c <= '9')

return f * x;

}int n, m, ans, a[1000005], tot, root[1000005];

struct node

q[20000005];

inline int newnode(int x)

int jianshu(int node, int beg, int en)

int mid = (beg + en) >> 1;

q[node].l = jianshu(q[node].l, beg, mid);

q[node].r = jianshu(q[node].r, mid + 1, en);

return node;

}int modify(int node, int beg, int en, int x, int y)

return node;

}int getans(int node, int beg, int en, int x)

}int main()

else

}return 0;

}

主席樹和可持久化陣列的**很短,但是,理解思路才是最重要的,它們的思想也很重要。

主席樹學習筆記

問題 給定乙個n個數的序列,q次詢問第x個數到第y個數中的第k最值。我們假定是第k小。為了使討論更加簡便,我們假定序列的每個數都是不大於n的正整數。當然一般題目中元素範圍很大,但是可以用離散化預處理來做到這一點。考慮乙個比較高階的做法 令g i j 為前i個數中,值為j的數的個數。很容易用o n 2...

主席樹 學習筆記

主席樹就是權值線段樹的乙個集合 模板題為求某個區間的第 k 大,權值線段樹也有求第 k 大這個功能,但是不能維護區間,只能求整個全域性第 k 大 所以學習這個之前,務必先搞懂權值線段樹 所以 都是這道題的 模板 可持久化線段樹 1 主席樹 1 先從建樹開始說起 int build int l,int...

主席樹學習筆記

學習博文 主席樹總結 p3834 模板 可持久化線段樹 2 主席樹 給出乙個序列,每次詢問給定區間內第k小的值。主席樹模板。考慮最簡單的情況,也就是查詢區間固定。首先對資料進行離散化,用線段樹維護。每個節點對應離散化後值域的數的總個數 size.從上到下進行查詢時,判斷當前節點左子樹的 size 和...