【模板】Splay总结
rentenglong大佬写的splay的梳理使我受益颇丰,特此做出一定的总结。
数据结构
定义了一个struct结构体。
为了在splay操作下储存根节点,我们宏定义了root 为 tree[0].ch[1],则我们把tree[0]看作了整棵树的超级根,而tree[0]的右儿子为排序树的根节点,那么一定有tree[0].v < tree[tree[0].ch[1]].v,所以我们需要将tree[0].v赋值为无穷小。
struct Node{
int v, father; //v 保存的值, fa 该点的父亲结点
int ch[]; //ch[0] 左孩子(小于根) ch[1] 右孩子(大于根)
int sum; //以该结点为子树的所有数的出现次数
int recy; //该结点所代表的数的出现次数(记录重复次数)
}tree[MAX];
另外我们用两个变量n(搜索树的结点个数),points(整棵树中所有数的储存次数)。
更新当前结点的sum值
在进行splay操作后整棵树的结构可能会发生改变,那么有些结点的sum值就有可能会发生改变,我们需要维护sum的原有定义,所以我们要把当前结点的sum给更新一下。
void updata(int x)
{
tree[x].sum = tree[tree[x].ch[]].sum + tree[tree[x].ch[]].sum + tree[x].recy;
}
获取父子关系
用于确定当前结点是父亲结点的左孩子(ch[0])还是右孩子(ch[1])。左孩子小于父亲结点,右孩子大于父亲结点。
int identify(int x)
{
return tree[tree[x].father].ch[] == x ? : ;
}
建立父子关系
用于连接两个点,其中一个点是另一个点的孩子。connect(x, f, son(0/1))表示 x 是 f 的 son(0/1) 孩子(0表示左,1表示右)。
void connect(int x, int f, int son)
{
tree[x].father = f;
tree[f].ch[son] = x;
}
旋转操作
所谓旋转指的是把指定结点向上移动一级然后将其父亲结点作为他的儿子。
通过异或操作可以进行。
void rotate(int x)
{
int y = tree[x].father;
int mroot = tree[y].father;
int mrootson = identify(y);
int yson = identify(x);
int b = tree[x].ch[yson ^ ];
connect(b, y, yson), connect(y, x, (yson ^ )), connect(x, mroot, mrootson);
updata(y), updata(x);
}
伸展操作
把我们要用的结点旋转到根上来。
考虑双旋和单旋,节省时间。
void splay(int at, int to)
{
to = tree[to].father;
for(; tree[at].father != to;)
{
int up = tree[at].father;
if(tree[up].father == to) rotate(at);
else if(identify(up) == identify(at)) rotate(up), rotate(at);
else rotate(at), rotate(at);
}
}
新建结点&破坏处理废物结点
int crepoint(int v, int father)
{
n ++;
tree[n].v = v;
tree[n].father = father;
tree[n].sum = tree[n].recy = ;
return n;
}
void destory(int x)
{
tree[x].v = tree[x].ch[] = tree[x].ch[] = tree[x].father = tree[x].recy = tree[x].sum = ;
if(x == n) n --;
}
内置的插入结点
int build(int v)
{
points ++;
if(n == )
{
root = ;
crepoint(v, );
}
else
{
int now = root;
for(;;)
{
if(now) tree[now].sum ++;
if(v == tree[now].v)
{
tree[now].recy ++;
return now;
}
int Next = v < tree[now].v ? : ;
if(!tree[now].ch[Next])
{
crepoint(v, now);
tree[now].ch[Next] = n;
return n;
}
now = tree[now].ch[Next];
}
}
return ;
}
外部调用的查找结点
int find(int v)
{
int now = root;
for(;;)
{
if(tree[now].v == v)
{
splay(now, root);
return now;
}
int Next = v < tree[now].v ? : ;
if(!tree[now].ch[Next]) return ;
now = tree[now].ch[Next];
}
}
外部调用的插入结点&删除结点
void Push(int v)
{
int add = build(v);
splay(add, root);
}
void Pop(int v)
{
int deal = find(v);
if(!deal) return;
points --;
if(tree[deal].recy > )
{
tree[deal].recy --;
tree[deal].sum --;
return;
}
if(!tree[deal].ch[])
{
root = tree[deal].ch[];
tree[root].father = ;
}
else
{
int Left = tree[deal].ch[];
for(; tree[Left].ch[];) Left = tree[Left].ch[];
splay(Left, tree[deal].ch[]);
int Right = tree[deal].ch[];
connect(Right, Left, ), connect(Left, , );
updata(Left);
}
destory(deal);
}
查询第v大&查询v是第几大
int Rank(int v)//查询v的排名
{
int ans = , now = root;
for(;;)
{
if(tree[now].v == v) return ans + tree[tree[now].ch[]].sum + ;
if(now == ) return ;
if(v < tree[now].v) now = tree[now].ch[];
else{
ans = ans + tree[tree[now].ch[]].sum + tree[now].recy;
now = tree[now].ch[];
}
}
if(now) splay(now, root);
return ;
}
int At_rank(int x)//查询排名为x的数
{
if(x > points) return -INF;
int now = root;
for(;;)
{
int minused = tree[now].sum - tree[tree[now].ch[]].sum;
if(x > tree[tree[now].ch[]].sum && x <= minused) break;
if(x < minused) now = tree[now].ch[];
else{
x = x - minused;
now = tree[now].ch[];
}
}
splay(now, root);
return tree[now].v;
}
查找前驱&后继
int Upper(int v)
{
int now = root;
int result = INF;
for(; now;)
{
if(tree[now].v > v && tree[now].v < result) result = tree[now].v;
if(v < tree[now].v) now = tree[now].ch[];
else now = tree[now].ch[];
}
return result;
}
int Lower(int v)
{
int now = root;
int result = -INF;
for(; now;)
{
if(tree[now].v < v && tree[now].v > result) result = tree[now].v;
if(v > tree[now].v) now = tree[now].ch[];
else now = tree[now].ch[];
}
return result;
}
整段代码(洛谷P3369)
#include<bits/stdc++.h>
using namespace std; const int MAX = ;
const int INF = ; class Splay
{
#define root tree[0].ch[1]
private:
struct Node{
int v, father; //v 保存的值, fa 该点的父亲结点
int ch[]; //ch[0] 左孩子(小于根) ch[1] 右孩子(大于根)
int sum; //以该结点为子树的所有数的出现次数
int recy; //该结点所代表的数的出现次数(记录重复次数)
}tree[MAX];
int n = , points = ;
void updata(int x)
{
tree[x].sum = tree[tree[x].ch[]].sum + tree[tree[x].ch[]].sum + tree[x].recy;
}
int identify(int x)
{
return tree[tree[x].father].ch[] == x ? : ;
}
void connect(int x, int f, int son)
{
tree[x].father = f;
tree[f].ch[son] = x;
}
void rotate(int x)
{
int y = tree[x].father;
int mroot = tree[y].father;
int mrootson = identify(y);
int yson = identify(x);
int b = tree[x].ch[yson ^ ];
connect(b, y, yson), connect(y, x, (yson ^ )), connect(x, mroot, mrootson);
updata(y), updata(x);
}
void splay(int at, int to)
{
to = tree[to].father;
for(; tree[at].father != to;)
{
int up = tree[at].father;
if(tree[up].father == to) rotate(at);
else if(identify(up) == identify(at)) rotate(up), rotate(at);
else rotate(at), rotate(at);
}
}
int crepoint(int v, int father)
{
n ++;
tree[n].v = v;
tree[n].father = father;
tree[n].sum = tree[n].recy = ;
return n;
}
void destory(int x)
{
tree[x].v = tree[x].ch[] = tree[x].ch[] = tree[x].father = tree[x].recy = tree[x].sum = ;
if(x == n) n --;
}
int build(int v)
{
points ++;
if(n == )
{
root = ;
crepoint(v, );
}
else
{
int now = root;
for(;;)
{
if(now) tree[now].sum ++;
if(v == tree[now].v)
{
tree[now].recy ++;
return now;
}
int Next = v < tree[now].v ? : ;
if(!tree[now].ch[Next])
{
crepoint(v, now);
tree[now].ch[Next] = n;
return n;
}
now = tree[now].ch[Next];
}
}
return ;
}
public :
int getroot() {return root;}
void init()
{
tree[].v = -INF;
}
int find(int v)
{
int now = root;
for(;;)
{
if(tree[now].v == v)
{
splay(now, root);
return now;
}
int Next = v < tree[now].v ? : ;
if(!tree[now].ch[Next]) return ;
now = tree[now].ch[Next];
}
}
void Push(int v)
{
int add = build(v);
splay(add, root);
}
void Pop(int v)
{
int deal = find(v);
if(!deal) return;
points --;
if(tree[deal].recy > )
{
tree[deal].recy --;
tree[deal].sum --;
return;
}
if(!tree[deal].ch[])
{
root = tree[deal].ch[];
tree[root].father = ;
}
else
{
int Left = tree[deal].ch[];
for(; tree[Left].ch[];) Left = tree[Left].ch[];
splay(Left, tree[deal].ch[]);
int Right = tree[deal].ch[];
connect(Right, Left, ), connect(Left, , );
updata(Left);
}
destory(deal);
}
int Rank(int v)
{
int ans = , now = root;
for(;;)
{
if(tree[now].v == v) return ans + tree[tree[now].ch[]].sum + ;
if(now == ) return ;
if(v < tree[now].v) now = tree[now].ch[];
else{
ans = ans + tree[tree[now].ch[]].sum + tree[now].recy;
now = tree[now].ch[];
}
}
if(now) splay(now, root);
return ;
}
int At_rank(int x)
{
if(x > points) return -INF;
int now = root;
for(;;)
{
int minused = tree[now].sum - tree[tree[now].ch[]].sum;
if(x > tree[tree[now].ch[]].sum && x <= minused) break;
if(x < minused) now = tree[now].ch[];
else{
x = x - minused;
now = tree[now].ch[];
}
}
splay(now, root);
return tree[now].v;
}
int Upper(int v)
{
int now = root;
int result = INF;
for(; now;)
{
if(tree[now].v > v && tree[now].v < result) result = tree[now].v;
if(v < tree[now].v) now = tree[now].ch[];
else now = tree[now].ch[];
}
return result;
}
int Lower(int v)
{
int now = root;
int result = -INF;
for(; now;)
{
if(tree[now].v < v && tree[now].v > result) result = tree[now].v;
if(v > tree[now].v) now = tree[now].ch[];
else now = tree[now].ch[];
}
return result;
}
#undef root
};
Splay F; inline int read()
{
int x = , w = ; char ch= getchar();
for(; !isdigit(ch); w |= (ch == '-'), ch = getchar());
for(; isdigit(ch); x = (x << ) + (x << ) + (ch ^ ), ch = getchar());
return w ? -x : x;
} int main()
{
int opt, n, x;
//scanf("%d", &n);
n = read();
F.init();
for(int i = ; i <= n; ++ i)
{
//scanf("%d%d", &opt, &x);
opt = read(), x = read();
switch(opt)
{
case : F.Push(x); break;
case : F.Pop(x); break;
case : printf("%d\n", F.Rank(x)); break;
case : printf("%d\n", F.At_rank(x)); break;
case : printf("%d\n", F.Lower(x)); break;
case : printf("%d\n", F.Upper((x))); break;
}
}
return ;
}
【模板】Splay总结的更多相关文章
- 算法模板——splay区间反转 2
实现功能:同splay区间反转 1(基于BZOJ3223 文艺平衡树) 这次改用了一个全新的模板(HansBug:琢磨了我大半天啊有木有),大大简化了程序,同时对于splay的功能也有所完善 这里面没 ...
- 算法模板——splay区间反转 1
实现的功能:将序列区间反转,并维护 详见BZOJ3223 var i,j,k,l,m,n,head,a1,a2:longint; s1:ansistring; a,b,c,d,fat,lef,rig: ...
- 【luogu P3369 普通平衡树(Treap/SBT)】 模板 Splay
题目链接:https://www.luogu.org/problemnew/show/P3369 #include <cstdio> #include <algorithm> ...
- 洛谷 P3391 模板Splay
#include<bits/stdc++.h> using namespace std; #define maxn 200000 int read() { ,w=; ;ch=getchar ...
- [模板] Splay
欠了好久的Splay,以后就它了. 默写真不容易,过几天估计就忘了.. 整个Splay真的精妙,不拖泥带水那种.. 前驱后继之所以不能用rk转到根,是因为这个数不一定存在.. kth中<=老忘记 ...
- 模板—splay
#include<iostream> #include<cstdio> #define cin(x) scanf("%d",&x) using na ...
- 模板——Splay
$Splay$ #include <bits/stdc++.h> #define inf (int)1e9 using namespace std; const int N=1e5+100 ...
- Splay 伸展树
废话不说,有篇论文可供参考:杨思雨:<伸展树的基本操作与应用> Splay的好处可以快速分裂和合并. ===============================14.07.26更新== ...
- [NOI2003][bzoj1507] 文本编辑器 editor [splay]
其实看明白了就是一道水题 毕竟模板 splay敲一发,插入一个串的时候先把它构建成一棵平衡树,再挂到原来的splay上面去即可 没别的了,上代码 #include<iostream> #i ...
- splay最终模板
来自wjmzbmr的splay模板 #include<cstdio> #include<iostream> #include<algorithm> using na ...
随机推荐
- step1: python & scrapy安装
#首先安装python,这里安装python所需依赖包yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-dev ...
- SP16580 QTREE7 - Query on a tree VII
Description 一棵树,每个点初始有个点权和颜色(0/1) 0 u :询问所有u,v 路径上的最大点权,要满足u,v 路径上所有点的颜色都相同 1 u :反转u 的颜色 2 u w :把u 的 ...
- 深入理解JavaScript系列(50):Function模式(下篇)
介绍 本篇我们介绍的一些模式称为初始化模式和性能模式,主要是用在初始化以及提高性能方面,一些模式之前已经提到过,这里只是做一下总结. 立即执行的函数 在本系列第4篇的<立即调用的函数表达式> ...
- HTTP 错误 404.11 - Not Found 请求筛选模块被配置为拒绝包含双重转义序列的请求。
一些URL中可能会包含+号等符号,然后IIS7以上的版本会默认拒绝请求此URL,需要进行如下的修改. <configuration> <system.webServer> &l ...
- CSS代码优化(转载)
要点1:css代码优化作用与意义 1.减少占用网页字节.在同等条件下缩短浏览器下载css代码时间,相当于加快网页打开速度:2.便于维护.简化和标准化css代码让css代码减少,便于日后维护:3.让自己 ...
- Java - 多线程中的不变性问题
这篇记录一下保证并发安全性的策略之——不变性. (注意:是Immutable,不是Invariant!) 将一连串行为组织为一个原子操作以保证不变性条件,或者使用同步机制保证可见性,以防止读到失效数据 ...
- 预防XSS方法:HtmlEncode和JavaScriptEncode(转)
XSS又称CSS,全称Cross SiteScript,跨站脚本攻击,是Web程序中常见的漏洞,XSS属于被动式且用于客户端的攻击方式,所以容易被忽略其危害性.其原理是攻击者向有XSS漏洞的网站中输入 ...
- Mac下使用Parallels Desktop安装CentOS操作系统
Mac下安装Centos后,Mac和Centos之间默认是不通的,需要做下面一些设置后,才能互相共享. 启用网络设置 默认的网络设置是没有enable的,因此,在安装完之后需要开启的话,需要保证开启& ...
- python使用元类
原文:https://blog.csdn.net/youzhouliu/article/details/51906158 type() 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义 ...
- Spring课程 Spring入门篇 5-6 introductions应用
1 解析 1.1 aop:declare-parents 标签简介 1.2 标签使用样式 2 代码演练 2.1 introductions标签应用 1 解析 1.1 aop:declare-paren ...