Loading docs/ds/treap.md +24 −10 Original line number Diff line number Diff line Loading @@ -53,16 +53,18 @@ node *merge(node *u, node *v) { 将一个有 $n$ 个节点的序列 $a_i$ 转化为一棵 treap 。 定义 `Merge(x,y)` 表示将根为 $x$ 和 $y$ 的两棵 Treap 合并成一棵,其根为该函数的返回值。需要保证 $x<y$ 。 定义 ```Merge(x,y)``` 表示将根为 $x$ 和 $y$ 的两棵 Treap 合并成一棵,其根为该函数的返回值。需要保证 $x<y$ 。 定义 `Split(o,k,x,y)` 表示将根为 $o$ 的 Treap 分裂成 $x,y$ 两个部分,保证 $x < y$ ,即将该 Treap 分裂成权值小于等于 $k$ 的项和其他,分别为 $x$ 和 $y$ 。 定义 ```Split(o,k,x,y)``` 表示将根为 $o$ 的 Treap 分裂成 $x,y$ 两个部分,保证 $x < y$ ,即将该 Treap 分裂成权值小于等于 $k$ 的项和其他,分别为 $x$ 和 $y$ 。 可以依次暴力插入这 $n$ 个节点,每次插入一个权值为 $v$ 的节点时,将整棵 treap 按照权值分裂成权值小于等于 $v$ 的和权值大于 $v$ 的两部分,然后新建一个权值为 $v$ 的节点,将两部分和新节点按从小到大的顺序依次合并,时间复杂度 $O(n\log_2 n)$ ,单点插入时间复杂度 $O(\log_2 n)$ 。 ```cpp void build(int n, int a[]) { void build(int n,int a[]) { int x,y; for (int i = 1; i <= n; i++) { for(int i=1;i<=n;i++) { split(rt,a[i],x,y); rt=merge(merge(x,newnode(a[i])),y); } Loading @@ -73,7 +75,7 @@ void build(int n, int a[]) { 方法一:直接将这 $n$ 个节点构造成一棵类线段树,即每次选取最中间的节点作为一段区间的树根,这样能保证树高为 $O(\log_2 n)$ 。然后对每个节点钦定优先值,保证其满足堆的性质。 方法二:直接将这 $n$ 个节点构造成一棵类线段树,即每次选取最中间的节点作为一段区间的树根,这样能保证树高为 $O(\log_2 n)$ 。然后给每个节点一个随机优先级,不保证其满足堆的性质。因为非旋式 treap 的优先级不是维护堆的性质,保证树高的,而是使 `merge` 操作更加随机一点,所以也是正确的。 方法二:直接将这 $n$ 个节点构造成一棵类线段树,即每次选取最中间的节点作为一段区间的树根,这样能保证树高为 $O(\log_2 n)$ 。然后给每个节点一个随机优先级,不保证其满足堆的性质。因为非旋式 treap 的优先级不是维护堆的性质,保证树高的,而是使 ```merge``` 操作更加随机一点,所以也是正确的。 方法三:观察到 treap 是一个笛卡尔树,利用笛卡尔树的 $O(n)$ 建树方法即可,用单调栈维护右脊柱即可。 Loading Loading @@ -282,3 +284,15 @@ int main() { return 0; } ``` ## 练习题 [luogu P3369 【模板】普通平衡树](https://www.luogu.org/problemnew/show/P3369) [luogu P3391 【模板】文艺平衡树(Splay)](https://www.luogu.org/problemnew/show/P3391) [luogu P2596 \[ZJOI2006\]书架](https://www.luogu.org/problemnew/show/P2596) [luogu P2042 \[NOI2005\]维护数列](https://www.luogu.org/problemnew/show/P2042) [CF 702F T-Shirts](http://codeforces.com/problemset/problem/702/F) Loading
docs/ds/treap.md +24 −10 Original line number Diff line number Diff line Loading @@ -53,16 +53,18 @@ node *merge(node *u, node *v) { 将一个有 $n$ 个节点的序列 $a_i$ 转化为一棵 treap 。 定义 `Merge(x,y)` 表示将根为 $x$ 和 $y$ 的两棵 Treap 合并成一棵,其根为该函数的返回值。需要保证 $x<y$ 。 定义 ```Merge(x,y)``` 表示将根为 $x$ 和 $y$ 的两棵 Treap 合并成一棵,其根为该函数的返回值。需要保证 $x<y$ 。 定义 `Split(o,k,x,y)` 表示将根为 $o$ 的 Treap 分裂成 $x,y$ 两个部分,保证 $x < y$ ,即将该 Treap 分裂成权值小于等于 $k$ 的项和其他,分别为 $x$ 和 $y$ 。 定义 ```Split(o,k,x,y)``` 表示将根为 $o$ 的 Treap 分裂成 $x,y$ 两个部分,保证 $x < y$ ,即将该 Treap 分裂成权值小于等于 $k$ 的项和其他,分别为 $x$ 和 $y$ 。 可以依次暴力插入这 $n$ 个节点,每次插入一个权值为 $v$ 的节点时,将整棵 treap 按照权值分裂成权值小于等于 $v$ 的和权值大于 $v$ 的两部分,然后新建一个权值为 $v$ 的节点,将两部分和新节点按从小到大的顺序依次合并,时间复杂度 $O(n\log_2 n)$ ,单点插入时间复杂度 $O(\log_2 n)$ 。 ```cpp void build(int n, int a[]) { void build(int n,int a[]) { int x,y; for (int i = 1; i <= n; i++) { for(int i=1;i<=n;i++) { split(rt,a[i],x,y); rt=merge(merge(x,newnode(a[i])),y); } Loading @@ -73,7 +75,7 @@ void build(int n, int a[]) { 方法一:直接将这 $n$ 个节点构造成一棵类线段树,即每次选取最中间的节点作为一段区间的树根,这样能保证树高为 $O(\log_2 n)$ 。然后对每个节点钦定优先值,保证其满足堆的性质。 方法二:直接将这 $n$ 个节点构造成一棵类线段树,即每次选取最中间的节点作为一段区间的树根,这样能保证树高为 $O(\log_2 n)$ 。然后给每个节点一个随机优先级,不保证其满足堆的性质。因为非旋式 treap 的优先级不是维护堆的性质,保证树高的,而是使 `merge` 操作更加随机一点,所以也是正确的。 方法二:直接将这 $n$ 个节点构造成一棵类线段树,即每次选取最中间的节点作为一段区间的树根,这样能保证树高为 $O(\log_2 n)$ 。然后给每个节点一个随机优先级,不保证其满足堆的性质。因为非旋式 treap 的优先级不是维护堆的性质,保证树高的,而是使 ```merge``` 操作更加随机一点,所以也是正确的。 方法三:观察到 treap 是一个笛卡尔树,利用笛卡尔树的 $O(n)$ 建树方法即可,用单调栈维护右脊柱即可。 Loading Loading @@ -282,3 +284,15 @@ int main() { return 0; } ``` ## 练习题 [luogu P3369 【模板】普通平衡树](https://www.luogu.org/problemnew/show/P3369) [luogu P3391 【模板】文艺平衡树(Splay)](https://www.luogu.org/problemnew/show/P3391) [luogu P2596 \[ZJOI2006\]书架](https://www.luogu.org/problemnew/show/P2596) [luogu P2042 \[NOI2005\]维护数列](https://www.luogu.org/problemnew/show/P2042) [CF 702F T-Shirts](http://codeforces.com/problemset/problem/702/F)