【学习笔记】(13) 平衡树——记住不的板子
Treap
Splay
无旋Treap——fhq treap
简介
就是没有旋转操作的 Treap,一些性质什么的都跟 Treap 类似。
算法介绍
(1)merge(x,y)
将两棵“有序”(x中元素的权值最大值小于 y 中元素权值最小值)的Treap合并成一棵。
int ch[N][2], sz[N], pri[N], val[N]; //val 为权值,pri 为优先级,sz 为子树大小 int merge(int x, int y){ if(!x || !y) return x + y; //若 x 的优先级小,则它应该新树的根 //由于 x 中元素都小于 y 中元素权值,所以合并 x 的右子树和 y if(pri[x] < pri[y]) return ch[x][1] = merge(ch[x][1], y), modify(x), x; //另一种情况 else return ch[y][0] = merge(x, ch[y][0]), modify(y), y; }
(2.1) Split(u, v, x, y)
根据权值 v 将 u 这棵 Treap 分为两棵平衡树 x ,y,其中 x 中元素权值均小于等于 v, y 中元素权值均大于 v。
void split(int u, int v, int &x, int &y){ if(!u) return x = y = 0, void(); if(val[u] <= v) x = u, split(ch[u][1], v, ch[u][1], y); //u 的权值比 v 小,说明 u 以及 u 的左子树都属于 x, 所以递归划分 u 的右子树 else y = u, split(ch[u][0], v, x, ch[u][0]); //另一种情况 modify(u); }
(2.2) Split(u, kth, x, y)
跟据子树大小将树 u 分成两棵树 x, y。 x 的大小为 k ,他包含树 u 中权值前 k 小的元素。
void split(int u, int k, int &x, int &y){ if(!u) return x = y = 0, void(); if(k <= sz[ch[u][0]]) y = u, split(ch[u][0], k, x, ch[u][0]); //左子树大小大于 k ,说明 u 以及 u 的右子树都在 y 中, 递归划分左子树 else x = u, split(ch[u][1], k - sz[ch[u][0]] - 1, ch[u][1], y); modify(u); }
(3) insert
int newnode(int v){ // 新建一个权值为 v 的节点 sz[++tot] = 1, pri[tot] = rnd(), val[tot] = v; return tot; } void insert(int k, int v){ //在以 rt 为根的 Treap 中 k 的位置插入一个值为 v 的元素 split(rt, k, r1, r2); //根据排名分 rt = merge(r1, merge(newnode(v), r2)); } void insert(int &u, int v){ //在以 u 为根的 Treap 中插入一个值为 v 的元素 split(u, v, r1, r2); //根据权值分 u = merge(r1, merge(newnode(v), r2)); } //上面两个 split 是不一样的
(4) delte(u,v)
删除 u 为根的 Treap 中权值为 v 的所有元素。
void del(int &u, int v){ int L, R, p, q; split(u, v, L, R); //调用 (2.1) 对应代码 split(L, v - 1, p, q); u = merge(L, merge(p, q)); }
注意:此操作会将所有权值为 v 的元素都删除掉,若只删除一个,则需要求出 v 的排名,在调用 split(u,k,x,y) 以便实现删除一个元素。
(5) modify(u,l,r)
对区间
void modify(int &u, int l, int r){ int L, R, p, q; split(u, l - 1, L, R); //调用 (2.2) 对应代码 split(R, r - l + 1, p, q); solve(p); u = merge(L, merge(p, q)); }
Merge(x, y)
合并两个 “乱序” Treap (即
复杂度
void Merge(int x, int y){ if(!x || !y) return x + y; if(pri[x] > pri[y]) swap(x, y); int L, R; split(v, val[u], L, R); //调用 (2.1) 对应代码 ch[x][0] = Merge(ch[x][0], L); ch[x][1] = Merge(ch[x][1], R); }
其他操作
1.删除
删除一个数
void del(int v){ int x, y, z; split(rt, v, x, z), split(x, v - 1, x, y); y = merge(ch[y][0], ch[y][1]); rt = merge(merge(x, y), z); }
2.第 大
查找第
int kth(int u, int k){ while(1){ if(k <= sz[ch[u][0]]) u = ch[u][0]; else if(k == sz[ch[u][0]] + 1) return u; else k -= sz[ch[u][0]] + 1, u = ch[u][1]; } }
3. 前缀/后继
查找
//前驱 split(rt, v - 1, x, y); printf("%d\n", val[kth(x, sz[x])]); rt = merge(x, y); //后继 split(rt, v, x, y); printf("%d\n", val[tr.kth(y, 1)]); rt = tr.merge(x, y);
4. 排名
查询
split(rt, v - 1, x, y); printf("%d\n", sz[x] + 1); rt = merge(x, y);
例题
Ⅰ. P3369 【模板】普通平衡树
模板,操作上面都讲过。
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } bool _u; mt19937 rnd((unsigned)time(0)); int n, tot, rt; int ch[N][2], sz[N], pri[N], val[N]; struct FHQ{ void modify(int x){sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;} int new_node(int v){sz[++tot] = 1, val[tot] = v, pri[tot] = rnd(); return tot;} int merge(int x, int y){ if(!x || !y) return x + y; if(pri[x] < pri[y]) return ch[x][1] = merge(ch[x][1], y), modify(x), x; else return ch[y][0] = merge(x, ch[y][0]), modify(y), y; } void split(int u, int v, int &x, int &y){ if(!u) return x = y = 0, void(); if(val[u] <= v) x = u, split(ch[u][1], v, ch[u][1], y); else y = u, split(ch[u][0], v, x, ch[u][0]); modify(u); } int kth(int u, int k){ while(1){ if(k <= sz[ch[u][0]]) u = ch[u][0]; else if(k == sz[ch[u][0]] + 1) return u; else k -= sz[ch[u][0]] + 1, u = ch[u][1]; } } }tr; bool _v; int main(){ cerr << abs(&_u - &_v) / 1048576.0 << " MB\n"; n = read(); while(n--){ int opt = read(), v = read(), x, y, z; if(opt == 1){ tr.split(rt, v, x, y); rt = tr.merge(tr.merge(x, tr.new_node(v)), y); } else if(opt == 2){ tr.split(rt, v, x, z), tr.split(x, v - 1, x, y); y = tr.merge(ch[y][0], ch[y][1]); rt = tr.merge(tr.merge(x, y), z); } else if(opt == 3){ tr.split(rt, v - 1, x, y); printf("%d\n", sz[x] + 1); rt = tr.merge(x, y); } else if(opt == 4){ printf("%d\n", val[tr.kth(rt, v)]); } else if(opt == 5){ tr.split(rt, v - 1, x, y); printf("%d\n", val[tr.kth(x, sz[x])]); rt = tr.merge(x, y); } else{ tr.split(rt, v, x, y); printf("%d\n", val[tr.kth(y, 1)]); rt = tr.merge(x, y); } } return 0; }
Ⅱ. P4146 序列终结者
为了写这篇博客,终于把6个月前没过的代码调出来了。
对于区间反转与区间加,直接打标记就行了。
#include<bits/stdc++.h> using namespace std; const int N = 1e6 + 51; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } mt19937 rnd((unsigned)time(0)); int n, m, rt, r1, r2, tot; int maxn[N], val[N], add[N], sz[N]; int ch[N][2], pri[N]; bool lazy[N]; int newnode(int v){ sz[++tot] = 1, pri[tot] = rnd(), val[tot] = v; maxn[tot] = v, add[tot] = 0, lazy[tot] = 0; return tot; } void pushup(int u){ sz[u] = sz[ch[u][1]] + sz[ch[u][0]] + 1; maxn[u] = val[u]; if(ch[u][1]) maxn[u] = max(maxn[u], maxn[ch[u][1]]); if(ch[u][0]) maxn[u] = max(maxn[u], maxn[ch[u][0]]); } void pushdown(int x){ if(lazy[x]){ lazy[ch[x][1]] ^= 1, lazy[ch[x][0]] ^= 1; swap(ch[x][1], ch[x][0]); lazy[x] = 0; } if(add[x]){ add[ch[x][1]] += add[x], add[ch[x][0]] += add[x]; val[ch[x][1]] += add[x], val[ch[x][0]] += add[x]; maxn[ch[x][1]] += add[x], maxn[ch[x][0]] += add[x]; add[x] = 0; } } int merge(int x, int y){ pushdown(x), pushdown(y); if(!x || !y) return x + y; int u; if(pri[x] < pri[y]) u = x, ch[x][1] = merge(ch[x][1], y); else u = y, ch[y][0] = merge(x, ch[y][0]); pushup(u); return u; } void split(int u, int k, int &x, int &y){ if(!u) return x = y = 0, void(); pushdown(u); if(sz[ch[u][0]] >= k) y = u, split(ch[u][0], k, x, ch[u][0]); else x = u, split(ch[u][1], k - sz[ch[u][0]] - 1, ch[u][1], y); pushup(u); } void insert(int k, int v){ split(rt, k, r1, r2), rt = merge(r1, merge(newnode(v), r2)); } int main(){ n = read(), m = read(); for(int i = 1; i <= n; ++i) insert(i - 1, 0); while(m--){ int opt = read(), l = read(), r = read(), v; int x, y, z; if(opt == 1){ v = read(); split(rt, l - 1, x, y); split(y, r - l + 1, y, z); val[y] += v, maxn[y] += v, add[y] += v; rt = merge(x, merge(y, z)); }else if(opt == 2){ split(rt, l - 1, x, y); split(y, r - l + 1, y, z); lazy[y] ^= 1; rt = merge(x, merge(y, z)); }else{ split(rt, l - 1, x, y); split(y, r - l + 1, y, z); printf("%d\n", maxn[y]); rt = merge(x, merge(y, z)); } } return 0; }
Ⅲ. P3960 [NOIP2017 提高组] 列队
开
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 3e5 + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } bool _u; mt19937 rnd((unsigned) time(0)); int n, m, q, cn1, cn2; int rt[N], ch[N << 1][2], rd[N << 1], sz[N << 1], val[N << 1], R[N]; int sum[N << 5], ls[N << 5], rs[N << 5]; void pushup(int u){ sz[u] = sz[ch[u][0]] + sz[ch[u][1]] + 1; } int newnode(int v){ int x = ++cn2; return val[x] = v, rd[x] = rnd(), sz[x] = 1, x; } int merge(int x, int y){ if(!x || !y) return x | y; if(rd[x] > rd[y]) return ch[x][1] = merge(ch[x][1], y), pushup(x), x; else return ch[y][0] = merge(x, ch[y][0]), pushup(y), y; } void split(int u, int k, int &x, int &y){ if(!u) return x = y = 0, void(); if(k <= sz[ch[u][0]]) y = u, split(ch[u][0], k, x, ch[u][0]); else x = u, split(ch[u][1], k - sz[ch[u][0]] - 1, ch[u][1], y); pushup(u); } void modify(int &u, int l, int r, int p){ if(!u) u = ++cn1; ++sum[u]; if(l == r) return ; int mid = (l + r) >> 1; if(p <= mid) modify(ls[u], l, mid, p); else modify(rs[u], mid + 1, r, p); } int query(int u, int l, int r, int k){ if(l == r) return l; int mid = (l + r) >> 1, v = (mid - l + 1) - sum[ls[u]]; if(k <= v) return query(ls[u], l, mid, k); else return query(rs[u], mid + 1, r, k - v); } bool _v; signed main(){ cerr << abs(&_u - &_v) / 1048576.0 << " MB\n"; //freopen("1.in", "r", stdin); //freopen("1.out", "w", stdout); n = read(), m = read(), q = read(); for(int i = 1, j = m; i <= n; ++i, j += m) rt[0] = merge(rt[0], newnode(j)); for(int i = 1; i <= q; ++i){ int x = read(), y = read(), res, a, b, c; if(y < m){ int ss = sz[rt[x]]; if(y < m - ss){ int cnt = query(R[x], 1, m - 1, y); printf("%lld\n", res = (x - 1) * m + cnt); modify(R[x], 1, m - 1, cnt); }else{ split(rt[x], y - (m - ss), a, b), split(b, 1, b, c); printf("%lld\n", res = val[b]), rt[x] = merge(a, c); } } split(rt[0], x - 1, a, b), split(b, 1, b, c); if(y < m) rt[x] = merge(rt[x], b); else printf("%lld\n", res = val[b]); rt[0] = merge(a, merge(c, newnode(res))); } return 0; }
重量平衡树
本文作者:南风未起
本文链接:https://www.cnblogs.com/jiangchen4122/p/17417676.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步