splay小结—植树结
我要把高级数据结构当爸爸了... ...弱到跪烂了。
splay,二叉搜索树的一种,具有稳定变形功能。
二叉搜索树:对于一个节点,都只有不超过2个孩子。其左子树内的点的权值都比这个点小,右子树的点的权值都比这个点要大(等于的话随你)。这个性质对于所有点都成立。
我们可以看到二叉搜索树适用于解决求前驱后继、求排名、求第k大等问题。但是如果出题人执(sang)意(xin)要(bing)卡(kuang),造出递减或递增的数据出来,那么你的常规二叉搜索树的每一次插入和查找都是O(n)的,会被时代淘汰的。我们只有提高自己的姿势水平才行。
splay也是一颗二叉搜索树。但是它有个优点:它可以通过旋转节点改变树的形状,江低树的直径,从而江低复杂度。从理论上看,这棵树的深度是logN的,所以一次查询的复杂度就是logN。
那么怎样进行旋转节点就是一个神奇的问题了!
我们要把节点x旋转到它的父亲y的位置,就可以找到规律:
如果x是左儿子,那么y就要变成x的右儿子,x原本的右儿子要变成y的左儿子。
如果x是右儿子,情况是差不多的。
这点东西都很好写,几个if就差不多了(所以常数很大)。关键是旋转技巧。
splay(x,goal),表示江x旋转到goal的儿子处。
如果x是goal的孙子,那么转一次就好了。
不是的话,我们就可以把x转到它的爷爷的位置上面(因为要江低复杂度和树的直径,别问我为什么,问塔尖去)。
你还可以在旋转顺序上做点优化来达到江低直径的效果。
上题。BZOJ书架 操作还是很全的。
但其实初学者还是像我一样先做 codevs营业额统计 比较好,操作较少但splay不少。
改变位置就相当于删除再插入。求排名就是把它splay到顶端然后看左子树大小。
第k本就是第k大,就是二叉搜索树的普通操作。
书架 代码如下。num应该改名成fact,表示点对应的书的编号。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <cstring>
#include <queue>
#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
using namespace std;
const int N = 300010;
int n,m,T[N],ch[N][2],size[N],num[N];
int fa[N],root,tot,pos[N],rec[N];
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
LL gl()
{
LL x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
int gsize(int x){return x?size[x]:x;}
void up(int x){size[x]=gsize(ch[x][0])+gsize(ch[x][1])+1;}
void rtt(int x,int kind) //综合版,包含了zig和zag。
{
int y=fa[x],z=fa[y];
ch[y][!kind]=ch[x][kind];fa[x]=z;
if(ch[x][kind])fa[ch[x][kind]]=y;
if(z)
if(ch[z][0]==y)ch[z][0]=x;
else ch[z][1]=x;
ch[x][kind]=y;fa[y]=x;
up(y);up(x);
}
void splay(int x,int goal)
{
if(x==goal)return;
while(fa[x]!=goal)
if(fa[fa[x]]==goal)
rtt(x,ch[fa[x]][0]==x);
else
{
int y=fa[x],kind=ch[fa[y]][0]==y;
if(ch[y][kind]==x)rtt(x,kind^1),rtt(x,kind);
else rtt(y,kind),rtt(x,kind); //听缩这样可以江低复杂度,实际上是的。
}
if(goal==0)root=x;
}
void newnode(int &r,int fat,int x)
{
r=++tot;fa[tot]=fat;size[tot]=1;
ch[tot][0]=ch[tot][1]=0;num[tot]=x;return;
}
void insert(int rt,int x,int value) //把一个编号为x的书放到位置value上
{
if(value==1 && ch[rt][0]==0){newnode(ch[rt][0],rt,x);return;}
if(value==2+gsize(ch[rt][0])&&ch[rt][1]==0){newnode(ch[rt][1],rt,x);return;}
if(value<=1+gsize(ch[rt][0]))insert(ch[rt][0],x,value);
else insert(ch[rt][1],x,value-gsize(ch[rt][0])-1);
up(x);
}
void del(int x)
{
splay(x,0);
if(ch[x][0]==0&&ch[x][1]==0){root=0;return;}
if(ch[x][0]==0){root=ch[x][1];fa[root]=0;return;}
if(ch[x][1]==0){root=ch[x][0];fa[root]=0;return;}
int tmp=ch[x][0];while(ch[tmp][1])tmp=ch[tmp][1];
splay(tmp,x);root=tmp;fa[tmp]=0;
ch[tmp][1]=ch[x][1];fa[ch[x][1]]=tmp;
up(tmp);
}
int getsize(int x){splay(x,0);return gsize(ch[x][0])+1;}
int gc()
{
char ch=getchar();
while(ch>'Z' || ch<'A')ch=getchar();
if(ch=='T')return 1;
if(ch=='B')return 2;
if(ch=='I')return 3;
if(ch=='A')return 4;
if(ch=='Q')return 5;
return 0;
}
void pr(int x)
{
printf(" point %d: ls=%d rs=%d size=%d\n",x,ch[x][0],ch[x][1],gsize(x));
if(ch[x][0])pr(ch[x][0]);if(ch[x][1])pr(ch[x][1]);
}
int find(int x)
{
int pt=x,r=root;
while(1)
{
if(pt==1+gsize(ch[r][0]))return num[r];
if(pt>1+gsize(ch[r][0]))pt-=(1+gsize(ch[r][0])),r=ch[r][1];
else r=ch[r][0];
}
return num[0];
}
int main()
{
n=gi();m=gi();
for(int i=1;i<=n;++i)
{
rec[i]=gi();
pos[rec[i]]=i;
if(i==1)newnode(root,0,rec[1]),root=tot;
else{insert(root,rec[i],i);if(i%2)splay(tot,0);}
}
for(int i=1;i<=m;++i)
{
int type=gc(),x=gi(),y=type==3?gi():0;
switch(type)
{
case 1:{del(pos[x]);insert(root,x,1);pos[x]=tot;splay(tot,0);break;}
case 2:{del(pos[x]);insert(root,x,n);pos[x]=tot;splay(tot,0);break;}
case 3:{int S=getsize(pos[x])+y;del(pos[x]);insert(root,x,S);splay(tot,0);pos[x]=tot;break;}
case 4:{printf("%d\n",getsize(pos[x])-1);break;}
case 5:{printf("%d\n",find(x));break;}
default:continue;
}
}
return 0;
}
splay小结—植树结的更多相关文章
- EditText使用详解-包含很多教程上看不到的功能演示
写道 标题有点大,说是详解,其实就是对EditText的一些常用功能的介绍,包括密码框,电话框,空白提示文字等等的讲解,尽量的介绍详细一点,也就是所谓的详解了..呵呵 广告一下我的应用“我团”,最新1 ...
- [转]Ubuntu中apt与apt-get命令的区别
转载于https://www.sysgeek.cn/apt-vs-apt-get/ Ubuntu 16.04 发布时,一个引人注目的新特性便是 apt 命令的引入.其实早在 2014 年,apt 命令 ...
- apt与apt-get命令的区别
apt 和 apt-get的区别 Debian 作为 Ubuntu.Linux Mint 和 elementary OS 等 Linux 操作系统的母板,其具有强健的「包管理」系统,它的每个组件和应用 ...
- apt 和 apt-get的区别
apt 和 apt-get的区别 - liudsl的博客 - CSDN博客 https://blog.csdn.net/liudsl/article/details/79200134 Linux软件 ...
- 小结-Splay
参照陈竞潇学长的模板写的BZOJ 3188: #include<cstdio> #include<cstring> #include<algorithm> #def ...
- Splay平衡树入门小结
学习到这部分算是数据结构比较难的部分了,平衡树不好理解代码量大,但在某些情况下确实是不可替代的,所以还是非学不可. 建议先学Treap之后在学Splay,因为其实Splay有不少操作和Treap差不多 ...
- c# 结课小结
C#总结知识点 模块一:知识点梳理 输入输出表达式---数据类型---变量与常量 ----运算符---语句-----数组与集合---函数--结构体: 模块二:输入与输出 输入: console.re ...
- pthread多线程编程的学习小结
pthread多线程编程的学习小结 pthread 同步3种方法: 1 mutex 2 条件变量 3 读写锁:支持多个线程同时读,或者一个线程写 程序员必上的开发者服务平台 —— DevSt ...
- 【BZOJ】2209: [Jsoi2011]括号序列(splay)
http://www.lydsy.com/JudgeOnline/problem.php?id=2209 splay又犯逗........upd1那里的sum忘记赋值反............. 本题 ...
随机推荐
- 在没有DOM操作的日子里,我是怎么熬过来的(中)
前言 继上篇推送之后,在掘金.segmentfault.简书.博客园等平台上迅速收到了不俗的反馈,大部分网友都留言说感同身受,还有不少网友追问中篇何时更新.于是,闰土顺应呼声,在这个凛冽的寒冬早晨,将 ...
- 【续】抓个Firefox的小辫子,jQuery表示不背这黑锅,Chrome,Edge,IE8-11继续围观中
引子 昨天我发了一篇文章[抓个Firefox的小辫子,围观群众有:Chrome.Edge.IE8-11],提到了一个Firefox很多版本都存在的问题,而相同的测试页面在Chrome.Edge.IE8 ...
- 基于Flink秒级计算时CPU监控图表数据中断问题
基于Flink进行秒级计算时,发现监控图表中CPU有数据中断现象,通过一段时间的跟踪定位,该问题目前已得到有效解决,以下是解决思路: 一.问题现象 以SQL02为例,发现本来10秒一 ...
- Tarjan算法:求解图的割点与桥(割边)
简介: 割边和割点的定义仅限于无向图中.我们可以通过定义以蛮力方式求解出无向图的所有割点和割边,但这样的求解方式效率低.Tarjan提出了一种快速求解的方式,通过一次DFS就求解出图中所有的割点和割边 ...
- Servlet之初始化参数和传递数据(ServletConfig,ServletContext )
ServletConfig 容器初始化一个Servlet的时候,会为这个Servlet建一个唯一的Servletconfig的对象(Servlet的配置对象) 容器会从部署的描述文件(web.xml) ...
- 教我徒弟Android开发入门(一)
前言: 这个系列的教程是为我徒弟准备的,也适合还不懂java但是想学android开发的小白们~ 本系列是在Android Studio的环境下运行,默认大家的开发环境都是配置好了的 没有配置好的同学 ...
- Jfinal启动原理及源码简析
以下所有源码只截取了部分代码,标题即为类名 1.Web.xml <filter-name>jfinal</filter-name> <filter-class>co ...
- Android 项目使用TensorFlow
Android 项目使用TensorFlow 首先需要搭建TensorFlow编译环境 参考:(http://www.cnblogs.com/dyufei/p/8027764.html) Tensor ...
- 守护进程VS守护线程
守护(daemon)进程 引入: join()方法可以使一个进程运行完之后再执行下一个进程,而daemon()方法就是主进程的代码执行完毕之后,不需要等待子进程,立即终止子进程. join()方法和d ...
- 用nodejs把目录下所有用px做单位的css文件转化为用rem做单位的css文件
20171105 1211/星期日 公司为了更好适配手机端,以前用px做单位的css文件,全部需要转化为用rem做单位,目前是1rem=37.5px;开发新项目时,还是用习惯的px写样式代码,完成UI ...