【ybt金牌导航4-6-2】【luogu P3835】可持久化平衡树

可持久化平衡树

题目链接:ybt金牌导航4-6-2 / luogu P3835

题目大意

要你支持一些操作。
插入数,删除数,查询数排名,查询某个排名的数,查询数的前驱后继。
但是它要求可以可持久化,即每次会在给定的历史版本上改动。

思路

其实可持续化平衡树和平衡树很像。
我们只用类比一下线段树和主席树,就不难想到要怎么搞了。

由于我们旋转就不好搞持久化,我们可以用没有旋转。
那当然是用无旋 Treap 啦。
大概的方法跟主席树差不多,就是拆树和合并数复制点来存值。

然后由于你之前的树不会被真的拆掉,当我们只是为了查询的时候我们可以不用拆掉再合并回去,我们只用让这次版本值你改动版本的根节点一样就行了。(反正你只是查询,树还是一模一样的)

代码

#include<queue> #include<cstdio> #include<cstdlib> using namespace std; int n, rt[500001], bb, op, x, tot; int ls[500001 << 6], rs[500001 << 6], val[500001 << 6], sz[500001 << 6], yj[500001 << 6]; int read() { int re = 0, zf = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') zf = -zf; c = getchar(); } while (c >= '0' && c <= '9') { re = (re << 3) + (re << 1) + c - '0'; c = getchar(); } return re * zf; } void write(int now) { if (now < 0) putchar('-'), now = -now; if (now > 9) write(now / 10); putchar(now % 10 + '0'); } void up(int now) { sz[now] = sz[ls[now]] + sz[rs[now]] + 1; } int newpoint(int num) { int re = ++tot; ls[re] = rs[re] = 0; val[re] = num; sz[re] = 1; yj[re] = rand(); return re; } int copypoint(int pl) {//复制之前的点 int re = ++tot; ls[re] = ls[pl]; rs[re] = rs[pl]; val[re] = val[pl]; sz[re] = sz[pl]; yj[re] = yj[pl]; return re; } pair <int, int> split_val(int now, int num) { if (!now) return make_pair(0, 0); pair <int, int> re; if (num < val[now]) { int noww = copypoint(now);//记得是可持续化,要复制点(下面也一样) re = split_val(ls[noww], num); ls[noww] = re.second; up(noww); re.second = noww; } else { int noww = copypoint(now); re = split_val(rs[noww], num); rs[noww] = re.first; up(noww); re.first = noww; } return re; } int merge(int x, int y) { if (!x) return y; if (!y) return x; if (yj[x] < yj[y]) { int xx = copypoint(x); rs[xx] = merge(rs[xx], y); up(xx); return xx; } else { int yy = copypoint(y); ls[yy] = merge(x, ls[yy]); up(yy); return yy; } } void insert(int bb, int num) { pair <int, int> x = split_val(rt[bb], num); int y = newpoint(num); rt[bb] = merge(merge(x.first, y), x.second); } void delete_(int bb, int num) { pair <int, int> x = split_val(rt[bb], num); pair <int, int> y = split_val(x.first, num - 1); y.second = merge(ls[y.second], rs[y.second]); rt[bb] = merge(merge(y.first, y.second), x.second); } //由于你可持续化不会把原来的真的割开,所以你不用重新合并,而是直接跟之前的根一样就可以(下面也一样) int ask_rnk(int bb, int num) { pair <int, int> x = split_val(rt[bb], num - 1); int re = sz[x.first] + 1; return re; } int ask_val(int now, int rnk) { while (now) { if (sz[ls[now]] >= rnk) now = ls[now]; else if (sz[ls[now]] + 1 == rnk) return val[now]; else { rnk -= sz[ls[now]] + 1; now = rs[now]; } } } int get_pre(int bb, int num) { pair <int, int> x = split_val(rt[bb], num - 1); if (!x.first) return -2147483647; return ask_val(x.first, sz[x.first]); } int get_nxt(int bb, int num) { pair <int, int> x = split_val(rt[bb], num); if (!x.second) return 2147483647; return ask_val(x.second, 1); } int main() { srand(19491001); n = read(); for (int i = 1; i <= n; i++) { bb = read(); op = read(); x = read(); rt[i] = rt[bb]; if (op == 1) { insert(i, x); continue; } if (op == 2) { delete_(i, x); continue; } if (op == 3) { write(ask_rnk(i, x)); putchar('\n'); continue; } if (op == 4) { write(ask_val(rt[i], x)); putchar('\n'); continue; } if (op == 5) { write(get_pre(i, x)); putchar('\n'); continue; } if (op == 6) { write(get_nxt(i, x)); putchar('\n'); continue; } } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_4-6-2.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(32)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示