口胡平衡树splay
文章目录
- 一.什么是平衡树
- 二.分步讲解
- 1.定义变量
- 4.预备函数
- 3.旋转操作
- 3.splay
- 4.插入
- 5.查询x的排名
- 6.查询排名x的数
- 7.前驱(比x小的最大的)
- 8.后继(比x大的最小的)
- 9.删除
- 三.模板
- 谢谢!
一.什么是平衡树
就这样给你说,平衡树就是一个节点的左儿子严格小于它,右儿子严格大于它的一棵二叉树,整个树的叶子结点的深度几乎一致。
二.分步讲解
1.定义变量
key[i]:排序的关键字
cnt[i]:每个节点的数的个数
f[i]:父节点
ch[i][0]:左儿子;ch[i][1]:右儿子
siz[i]:以i为根的子树大小
sz:节点总数
root:根节点的编号
4.预备函数
void clear (int x){//清零ch[x][0] = ch[x][1] = key[x] = cnt[x] = f[x] = siz[x] = 0;
}void update (int x){//更新siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + cnt[x];
}
3.旋转操作
给一张图就行了
看懂了吗?
void rotate (int x){int fa = f[x], gfa = f[fa], d = (x == ch[fa][1]);//d是找x是fa的哪一个儿子节点ch[fa][d] = ch[x][1 ^ d];f[ch[fa][d]] = fa;//***ch[x][1 ^ d] = fa;f[fa] = x;f[x] = gfa;if (gfa){int d1 = (fa == ch[gfa][1]);ch[gfa][d1] = x;}update (x), update (fa), update (gfa);//注意更新
}
3.splay
splay的作用就是把当前节点旋转到根,那么根就更新了,并且树是平衡的。
void splay (int x){for (int fa; fa = f[x]; rotate(x))if (f[fa])rotate ((ch[f[fa]][1] == fa) == (ch[fa][1] == x) ? fa : x);//这里要注意,如果当前节点,父亲节点和爷爷节点在一条线上,就要先旋转父亲节点,再旋转当前节点,注意不能失衡root = x;
}
4.插入
void insert (int x){if (! root){//第一种情况,是空树sz ++;ch[sz][0] = ch[sz][1] = f[sz] = 0;cnt[sz] = siz[sz] = 1;key[sz] = x;root = sz;return ;}int now = root, fa = 0;while (1){//第二种情况,要插到叶节点去if (key[now] == x){cnt[now] ++;update (now);update (fa);splay (now);//注意要旋转到根部return ;}fa = now;now = ch[now][x > key[now]];if (! now){sz ++;ch[sz][0] = ch[sz][1] = 0;f[sz] = fa;key[sz] = x;cnt[sz] = siz[sz] = 1;ch[fa][x > key[fa]] = sz;update (fa);splay (sz);return ;}}
}
5.查询x的排名
int find (int x){int ans = 0, now = root;while (1){if (key[now] > x){//如果当前节点关键字比x大,则查找左儿子now = ch[now][0];}else{ans += ch[now][0] ? siz[ch[now][0]] : 0;if (x == key[now]){//找到了splay (now);return ans + 1;}ans += cnt[now];now = ch[now][1];//以此类推}}
}
6.查询排名x的数
int findx (int x){//很简单,容易理解int now = root;while (1){if (ch[now][0] && siz[ch[now][0]] >= x)now = ch[now][0];else{x -= ch[now][0] ? siz[ch[now][0]] : 0;if (x <= cnt[now])return key[now];x -= cnt[now];now = ch[now][1];}}
}
7.前驱(比x小的最大的)
int pre (){int now = ch[root][0];while (ch[now][1])now = ch[now][1];return now;
}
8.后继(比x大的最小的)
int nex (){int now = ch[root][1];while (ch[now][0])now = ch[now][0];return now;
}
9.删除
void del (int x){//一共有四种情况,详见下find (x);//先把当前节点旋到根if (cnt[root] > 1){//如果当前节点的个数不止一个,就直接减cnt[root] --;return ;}if (! ch[root][0] && ! ch[root][1]){//如果当前节点没有子节点,就直接删clear (root);root = 0;return ;}//如果只有一个子节点,就让其子节点成为根if (! ch[root][0]){int oldroot = root;root = ch[root][1];f[root] = 0;clear (oldroot);return ;}else if (! ch[root][1]){int oldroot = root;root = ch[root][0];f[root] = 0;clear (oldroot);return ;}//如果有两个子节点,就把左面最大的后辈旋到根,然后更新节点信息就行了int oldroot = root, furoot = pre ();splay (furoot);ch[root][1] = ch[oldroot][1];f[ch[oldroot][1]] = root;update (root);clear (oldroot);
}
三.模板
此题是模板题:【模板】普通平衡树
Code:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;#define M 100005int n, ch[M][2], key[M], cnt[M], f[M], siz[M], root, sz;void clear (int x){ch[x][0] = ch[x][1] = key[x] = cnt[x] = f[x] = siz[x] = 0;
}
void update (int x){siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + cnt[x];
}
void rotate (int x){int fa = f[x], gfa = f[fa], d = (x == ch[fa][1]);ch[fa][d] = ch[x][1 ^ d];f[ch[fa][d]] = fa;//***ch[x][1 ^ d] = fa;f[fa] = x;f[x] = gfa;if (gfa){int d1 = (fa == ch[gfa][1]);ch[gfa][d1] = x;}update (x), update (fa), update (gfa);
}
void splay (int x){for (int fa; fa = f[x]; rotate(x))if (f[fa])rotate ((ch[f[fa]][1] == fa) == (ch[fa][1] == x) ? fa : x);root = x;
}
void insert (int x){if (! root){sz ++;ch[sz][0] = ch[sz][1] = f[sz] = 0;cnt[sz] = siz[sz] = 1;key[sz] = x;root = sz;return ;}int now = root, fa = 0;while (1){if (key[now] == x){cnt[now] ++;update (now);update (fa);splay (now);return ;}fa = now;now = ch[now][x > key[now]];if (! now){sz ++;ch[sz][0] = ch[sz][1] = 0;f[sz] = fa;key[sz] = x;cnt[sz] = siz[sz] = 1;ch[fa][x > key[fa]] = sz;update (fa);splay (sz);return ;}}
}
int find (int x){int ans = 0, now = root;while (1){if (key[now] > x){now = ch[now][0];}else{ans += ch[now][0] ? siz[ch[now][0]] : 0;if (x == key[now]){splay (now);return ans + 1;}ans += cnt[now];now = ch[now][1];}}
}
int findx (int x){int now = root;while (1){if (ch[now][0] && siz[ch[now][0]] >= x)now = ch[now][0];else{x -= ch[now][0] ? siz[ch[now][0]] : 0;if (x <= cnt[now])return key[now];x -= cnt[now];now = ch[now][1];}}
}
int pre (){int now = ch[root][0];while (ch[now][1])now = ch[now][1];return now;
}
int nex (){int now = ch[root][1];while (ch[now][0])now = ch[now][0];return now;
}
void del (int x){find (x);if (cnt[root] > 1){//***cnt[root] --;return ;}if (! ch[root][0] && ! ch[root][1]){clear (root);root = 0;return ;}if (! ch[root][0]){int oldroot = root;root = ch[root][1];f[root] = 0;clear (oldroot);return ;}else if (! ch[root][1]){int oldroot = root;root = ch[root][0];f[root] = 0;clear (oldroot);return ;}int oldroot = root, furoot = pre ();splay (furoot);ch[root][1] = ch[oldroot][1];f[ch[oldroot][1]] = root;update (root);clear (oldroot);
}
int main (){scanf ("%d", &n);while (n --){int opt, x;scanf ("%d %d", &opt, &x);if (opt == 1)insert (x);if (opt == 2)del (x);if (opt == 3)printf ("%d\n", find (x));if (opt == 4)printf ("%d\n", findx (x));if (opt == 5){insert (x);printf ("%d\n", key[pre ()]);del (x);}if (opt == 6){insert (x);printf ("%d\n", key[nex ()]);del (x);}}return 0;
}
谢谢!
口胡平衡树splay相关推荐
- 口胡fhq treap
口胡 其实就是传说中的无旋treap.鉴于我总是写不出无旋treap,但是无旋treap又意外的好用,而且之前这个无旋treap板子是在远航大佬的博客上学的,但是远航貌似现在没时间维护博客,所以我就发 ...
- [51Nod 1816] 小C的二分图 口胡
Description 小C有一个特殊的二分图(有着X部与Y部). 对于一个X部的点x,对应在Y部的相邻点只会是一个连续区间. 然后你需要找一个最大匹配,这个匹配经过小C的膜法也变得特殊了. 两个匹配 ...
- 关于LCT的一些口胡
LCT无疑是一个毒瘤..因为它巨巨巨巨巨难写QAQ(这当然是对于我这种splay都还没怎么写熟的小蒟蒻来说QAQ巨佬可以无视这篇博客了QAQ) 首先它维护了一些虚虚实实的链以此来维护动态森林..我们可 ...
- CF Round #681(Div.2)/CF1443 口胡题解
由于一些原因(时间),本蒟蒻决定口胡这场比赛. 下面的题解纯口胡,但是与题解中的解法基本相同. Solution A 答案就是2n+2,2n+4,2n+6--4n2n+2, 2n+4, 2n+6--4 ...
- 牛客小白月赛8(口胡和一些瞎想)
一些口胡题解 E 诡异数字 开场看的题,嗯,一个数位DP模板题,就是约束条件要好好想一下,其实也就取个min吧(?),懒得写,不知道为什么最后没几个人写,可能是dalao都不屑打小白赛吧 F 数列操作 ...
- hihocoder #1329 : 平衡树·Splay
#1329 : 平衡树·Splay 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho:小Hi,上一次你跟我讲了Treap,我也实现了.但是我遇到了一个关键的问题. ...
- 二分图相关结论及口胡证明
考虑点集A,B二分图 最小点覆盖: 概念:用最少的点覆盖二分图中所有边. 结论:最小覆盖点=最大匹配 证明:选择点集A所有匹配点,如果还存在一条边的两个端点都不在匹配点中那么让该两点匹配则最大匹配数目 ...
- [2015国家集训队互测]口胡
比赛链接 http://uoj.ac/contest/11 口胡题解 A.[集训队互测2015]Robot 直接果断打暴力了...这个暴力很好写,我就不废话了 B.[集训队互测2015]Marketi ...
- Topcoder口胡记 SRM 562 Div 1 ~ SRM 599 Div 1
据说做TC题有助于提高知识水平? :) 传送门:https://284914869.github.io/AEoj/index.html 转载请注明链接:http://www.cnblogs.com/B ...
- 洛谷 P2762 太空飞行计划问题(自己理解的口胡版)
题目描述 W 教授正在为国家航天中心计划一系列的太空飞行.每次太空飞行可进行一系列商业性实验而获取利润.现已确定了一个可供选择的实验集合E={E1,E2,-,Em},和进行这些实验需要使用的全部仪器的 ...
最新文章
- android 图片二维码识别和保存(二)
- 利用python爬虫(part10)--Xpath节点集与函数
- 用框架的你,可能早已忽略了这些事件API
- Hanlp的安装和配置
- 【LeetCode笔记】33. 搜索旋转排序数组(Java、二分法)
- python︱ collections模块(namedtuple/defaultdict/OrderedDict等)
- C++ 遍历文件夹下所有文件的多种方法
- 计算机共享文件夹拒绝访问权限,设置共享文件夹访问权限 拒绝访问的方法
- Qt 5.9 mysql 驱动加载失败解决办法
- 男怕入错行 完美池宇峰畅谈创业点滴
- NTFS交换数据流隐藏文件
- javaee实验:使用mvc模式 设计一个图书管理系统
- 来自原CSDN排名第一博主:成功的背后!(给所有IT人)
- 线性探测法和平方探测法 - 哈希表 - 完整代码
- 0基础如何学习安卓开发
- rabbitMQ-server控制台安装报错启动失败,黑窗口一闪即过Applying plugin configuration to rabbit .. failed.
- 纸牌游戏炸金花设计制作(C语言)
- %CRYPTO-4-RECVD_PKT_INV_SPI: decaps: rec'd IPSEC packet has invalid spi 解法方法
- win7纯净系统安装
- 《计算机应用基础》第四次作业,[业务]计算机应用基础四次小作业