這篇文章是我對學***樹的乙個小結。
當然,筆者很菜,除了模板就什麼都不會打,也會有一些知識理解得不夠深刻。
因此,這篇文章以後還會不定期更新的。
主席樹,全名稱為可持久化線段樹,也就是把每個階段的線段樹儲存起來,每次可以直接訪問乙個階段的狀態,是神犇\(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
輸出樣例#16405
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 和...