[SMOJ2165]treap
来源:互联网 发布:淘宝加入放心淘的条件 编辑:程序博客网 时间:2024/06/06 07:05
Treap 模板题,插入和删除参见 Treap总笔记,这里简要谈谈如何在一棵 Treap 中求第
我们不妨在每一个结点
为什么要维护这两个值呢?是为了方便我们后面的判断。
对于在当前以
- 当
k≤sizel 时,考虑到左子树的值均小于当前结点,则答案一定在左子树中,递归到左子树找第k 小 - 当
sizel<k≤sizel+cntu 时,要求的正好是当前重复的一段,当前结点即为答案,返回当前结点的值 - 不满足上述两种情况,答案在右子树,但因为当前子树中已有
sizel+cntu 个数肯定小于答案,因此问题转化为求右子树第k−sizel−cntu 小
这样即可在
需要注意的是,在代码中,需要在插入、删除和旋转后重新维护上述两个值(前两者是因为子树中对应的值已经发生了变化,旋转是因为子树的关系变化)。原则上必须按照自底向上的顺序维护,否则会出问题。因此旋转后,应先维护原根结点的值(现在它是子结点),再维护被旋上去的结点的值(现在它是根)。
参考代码一(懒删除):
#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>using namespace std;struct Tnode { Tnode *child[2]; int fix, val; int siz, cnt; Tnode (int v = 0) : fix(rand()), val(v), siz(1), cnt(1) { //构造函数 child[0] = child[1] = 0; } void update() { //维护子树信息 int lsiz = (child[0] ? child[0] -> siz : 0); //注意要子结点非空才能取值,否则读非法内存 int rsiz = (child[1] ? child[1] -> siz : 0); siz = cnt + lsiz + rsiz; }};struct Treap { Tnode *root; Treap() { root = 0;/*new Tnode;*/ } void rotate(Tnode *&cur, int dir) { //dir=0左旋,1右旋 Tnode *ch = cur -> child[dir ^ 1]; cur -> child[dir ^ 1] = ch -> child[dir]; ch -> child[dir] = cur; cur -> update(); //自下向上的维护顺序 ch -> update(); cur = ch; } void insert_val(Tnode *&cur, int v) { if (cur == NULL) cur = new Tnode(v); else { if (v == cur -> val) ++(cur -> cnt); //重复结点不新增 else { int t = v > cur -> val; insert_val(cur -> child[t], v); if (cur -> child[t] -> fix < cur -> fix) rotate(cur, t ^ 1); //维护修正值的最小堆性质 } cur -> update(); } } int query_kth(Tnode *&cur, int k) { if (cur == NULL) return 0; //当前为空时及时返回 0,否则下面读非法内存 if (k > cur -> siz) return 0; //要查的数排名比整个子树大小还大,无解 int lsiz = (cur -> child[0] ? cur -> child[0] -> siz : 0); //分类讨论 if (k <= lsiz) return query_kth(cur -> child[0], k); else if (k <= lsiz + cur -> cnt) return cur -> val; else return query_kth(cur -> child[1], k - lsiz - cur -> cnt); } void remove_val(Tnode *&cur, int v) { if (cur -> val == v) { //只改动标记 -- cur -> cnt; -- cur -> siz; return; } remove_val(cur -> child[v > cur -> val], v); //直接修改标记就不需要旋转了 cur -> update(); //删完维护一下信息 }} lkb_treap;int main(void) { freopen("2165.in", "r", stdin); freopen("2165.out", "w", stdout); int N; scanf("%d", &N); while (N--) { int ch, k; scanf("%d%d", &ch, &k); if (ch == 1) lkb_treap.insert_val(lkb_treap.root, k); else if (ch == 2) printf("%d\n", lkb_treap.query_kth(lkb_treap.root, k)); else lkb_treap.remove_val(lkb_treap.root, k); } return 0;}
参考代码二(旋转删除):
#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>using namespace std;struct Tnode { Tnode *child[2]; int fix, val; int siz; Tnode (int v = 0) : fix(rand()), val(v), siz(1) { child[0] = child[1] = 0; } void update() { int lsiz = (child[0] ? child[0] -> siz : 0); int rsiz = (child[1] ? child[1] -> siz : 0); siz = lsiz + 1 + rsiz; }};struct Treap { Tnode *root; Treap() { root = 0;/*new Tnode;*/ } void rotate(Tnode *&cur, int dir) { //dir=0左旋,1右旋 Tnode *ch = cur -> child[dir ^ 1]; cur -> child[dir ^ 1] = ch -> child[dir]; ch -> child[dir] = cur; cur -> update(); ch -> update(); cur = ch; } void insert_val(Tnode *&cur, int v) { if (cur == NULL) cur = new Tnode(v); else { int t = v > cur -> val; //插入到哪棵子树 insert_val(cur -> child[t], v); //小于等于的都插左边,重复也新增 if (cur -> child[t] -> fix < cur -> fix) rotate(cur, t ^ 1); cur -> update(); } } int query_kth(Tnode *&cur, int k) { if (cur == NULL) return 0; if (k > cur -> siz) return 0; int lsiz = (cur -> child[0] ? cur -> child[0] -> siz : 0); if (k <= lsiz) return query_kth(cur -> child[0], k); else if (k == lsiz + 1) return cur -> val; else return query_kth(cur -> child[1], k - lsiz - 1); } void remove_val(Tnode *&cur, int v) { if (v == cur -> val) { //分类讨论 if (!cur -> child[0] && !cur -> child[1]) cur = NULL; //叶子结点直接删 else if (cur -> child[0] && cur -> child[1]) { //有两个儿子 int t = cur -> child[0] -> fix < cur -> child[1] -> fix; //选修正值更小的代替自己以维护其最小堆性质 rotate(cur, t); remove_val(cur -> child[t], v); //把旧的根旋下去之后递归删除 cur -> update(); } else if (cur -> child[0]) cur = cur -> child[0]; else cur = cur -> child[1]; //链结点直接代替 } else { remove_val(cur -> child[v > cur -> val], v); cur -> update(); } }} lkb_treap;int main(void) { freopen("2165.in", "r", stdin); freopen("2165.out", "w", stdout); int N; scanf("%d", &N); while (N--) { int ch, k; scanf("%d%d", &ch, &k); if (ch == 1) lkb_treap.insert_val(lkb_treap.root, k); else if (ch == 2) printf("%d\n", lkb_treap.query_kth(lkb_treap.root, k)); else lkb_treap.remove_val(lkb_treap.root, k); } return 0;}
阅读全文
0 0
- [SMOJ2165]treap
- Treap
- Treap
- Treap
- Treap
- Treap
- Treap
- treap
- Treap
- treap
- Treap
- treap
- Treap
- Treap
- treap
- Treap
- Treap
- Treap
- CvStatModel
- GUI——练习(打开保存文件)
- 分布式哈希算法
- Zookeeper全解析—Paxos灵魂
- JVM调优工具
- [SMOJ2165]treap
- 定时器的几种使用方式。
- HDU 6092-Rikka with Subset
- Ubuntu14.04虚拟机下安装VMwareTools
- linux别名alias命令
- ubuntu codeblocks通过配置文件修改主题
- DSP芯片的基本结构
- git 提交本地文件到coding.net 简单操作
- (一)HBase学习之路