BZOJ 1269 文本编辑器editor(伸展树)
题意
https://www.lydsy.com/JudgeOnline/problem.php?id=1269
思路
伸展树(\(\text{splay}\))功能比较齐全的模板,能较好的体现 \(\text{splay}\) 的功能,简单介绍一下 \(\text{splay}\)。
基本的概念和函数
\(\text{splay}\) 是平衡树的一种,能在均摊 \(\log n\) 的时间复杂度内完成很多序列操作(序列就是树的中序遍历),核心是以下两个函数。
rotate
首先是旋转函数,\(\text{rotate}(x)\) 表示旋转 \(x\) 节点到它父亲的位置,它的父亲变成它的孩子,在旋转函数的过程中,原树的中序遍历保持不变,代码如下:
void rotate(int x)
{
int y=fa[x],z=fa[y],k=(x==ch[y][1]);
if(z)ch[z][y==ch[z][1]]=x; fa[x]=z;
ch[y][k]=ch[x][!k]; if(ch[x][!k])fa[ch[x][!k]]=y;
ch[x][!k]=y,fa[y]=x;
push_up(y),push_up(x); //有时需要
}
网上有大量旋转函数过程图,这里不再解释。另外函数中的两个判断语句其实可以不用,零节点有儿子或父亲并不会造成错误,只是后面的 \(\text{LCT}\) 需要判断。
splay
然后是伸展函数,\(\text{splay}(x,w)\) 表示将 \(x\) 通过 \(\text{rotate}\) 函数上旋至成为 \(w\) 的儿子,特别的,当 \(w=0\) 时,表示将 \(x\) 上旋至根节点。
有一点特别注意,当节点 \(x\),\(x\) 的父亲 \(y\) ,\(y\) 的父亲 \(z\) 三点共线,需要先转 \(y\) 再转 \(x\) ,否则转两次 \(x\) 。可以通过模拟一条链的旋转发现这种方法的优越性。
代码如下,可以写的很短:
void splay(int x,int w)
{
while(fa[x]!=w)
{
int y=fa[x],z=fa[y];
if(z!=w)(x==ch[y][1])==(y==ch[z][1])?rotate(y):rotate(x);
rotate(x);
}
if(!w)rt=x;
}
注意在函数调用之前保证 \(x\) 以及 \(x\) 的祖先全部 \(\text{down}\) 完了。
基本的操作
构造
可以扔一个完美的 \(\text{splay}\) 上去,据说是对后面的操作在常数上有利。
void build(int &x,int f, int *arr,int l,int r)
{
if(l>r)return;
int mid=(l+r)>>1;
x=++tot;
ch[x][0]=ch[x][1]=0;
fa[x]=f;
pw[x]=arr[mid];
sz[x]=1,rev[x]=0; //在构造时清空会比较方便,如果有点权、标记的话要清空
build(ch[x][0],x,arr,l,mid-1);
build(ch[x][1],x,arr,mid+1,r);
push_up(x);
}
插入
对于维护中序权值单调的平衡树,通过这种方式实现插入函数。
void insert(int val)
{
int x=rt,y=0;
while(x&&val!=pw[x])y=x,x=ch[x][val>pw[x]];
if(!x)
{
x=++tot;
ch[x][0]=ch[x][1]=0;
fa[x]=y,ch[y][val>pw[y]]=x;
sz[x]=cnt[x]=1;
pw[x]=val;
}
else sz[x]++,cnt[x]++;
splay(x,0);
}
前驱后继
同样的,对于一个中序单调的 \(\text{splay}\) ,可以通过查找的方法获得前驱后继,这也就是找第一个大于/小于(等于)一个数 \(v\) 的方法,即在 \(\text{splay}\) 上二分。
int get_lss(int val)
{
int x=rt,y=0,tmp=-1,res=-1e9;
while(x)
{
if(pw[x]<val&&chk_max(res,pw[x]))tmp=x;
y=x,x=ch[x][val>pw[x]];
}
splay(y,0);
return tmp;
}
int get_grt(int val)
{
int x=rt,y=0,tmp=-1,res=1e9;
while(x)
{
if(pw[x]>val&&chk_min(res,pw[x]))tmp=x;
y=x,x=ch[x][val>=pw[x]];
}
splay(y,0);
return tmp;
}
当然对于中序不一定单调,维护一个普通序列的 \(\text{splay}\) ,可以从结构上分析,利用旋转操作中序不变的性质。
int get_pre(int x)
{
splay(x,0);
if(!ch[x][0])return -1;
x=ch[x][0];
while(ch[x][1])x=ch[x][1];
splay(x,0);
return x;
}
int get_nxt(int x)
{
splay(x,0);
if(!ch[x][1])return -1;
x=ch[x][1];
while(ch[x][0])x=ch[x][0];
splay(x,0);
return x;
}
第K大值
与动点线段树写法类似,直接二分即可。
int get_Kth(int K)
{
//K++; 因为有些题目需要加上极小值和极大值,为了下面调用的方便起见加了这一句话
int x=rt;
while(push_down(x),K!=sz[ch[x][0]]+1)
{
if(K<=sz[ch[x][0]])x=ch[x][0];
else K-=sz[ch[x][0]]+1,x=ch[x][1];
}
splay(x,0);
return x;
}
在这里说一下 \(\text{splay}\) 调用的时机,复杂度的证明我目前看不懂,但我知道由于 \(\text{splay}\) 是均摊 \(\log n\) ,所以只要是经过若干次循环迭代到的节点,都要上旋以防止复杂度堆积。
求一个点是第几大
void down_all(int x)
{
if(!x)return;
down_all(fa[x]);
push_down(x);
}
int get_rank(int x)
{
// down_all(x); 注意在自底向上操作一个点前,要保证标记传完
splay(x,0);
return sz[ch[x][0]]+1;
// return sz[ch[x][0]]; 同理,在有极小值时采用这种写法
}
区间操作
我们需要的就是“取出”一个区间,一般采取这样的方法:
l=get_Kth(l-1),r=get_Kth(r+1);
splay(l,0),splay(r,l);
这样之后,\(\text{ch}[r][0]\) 就是我们需要的区间了,区间打标记,求和,挪位什么的就很好实现了。
\(\text{splay}\) 也被叫做分裂树,它能实现区间的,挪位,这是它很显著的优势。
代码
#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
typedef long long ll;
const int N=3e6+5;
struct Splay
{
int ch[N][2],fa[N];char pw[N];
int sz[N];bool rev[N];
int rt,tot;
int operator [](const int x){return pw[get_Kth(x)];}
void init()
{
rt=tot=0;
ch[0][0]=ch[0][1]=fa[0]=sz[0]=rev[0]=0;
char str[2]={' ',' '};
build(rt,0,str,0,1);
}
void reved(int x)
{
rev[x]^=1;
std::swap(ch[x][0],ch[x][1]);
}
void push_up(int x){sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;}
void push_down(int x)
{
if(!rev[x])return;
if(ch[x][0])reved(ch[x][0]);
if(ch[x][1])reved(ch[x][1]);
rev[x]=0;
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=(x==ch[y][1]);
if(z)ch[z][y==ch[z][1]]=x; fa[x]=z;
ch[y][k]=ch[x][!k]; if(ch[x][!k])fa[ch[x][!k]]=y;
ch[x][!k]=y,fa[y]=x;
push_up(y),push_up(x);
}
void splay(int x,int w)
{
while(fa[x]!=w)
{
int y=fa[x],z=fa[y];
if(z!=w)(x==ch[y][1])==(y==ch[z][1])?rotate(y):rotate(x);
rotate(x);
}
if(!w)rt=x;
}
void build(int &x,int f,char *arr,int l,int r)
{
if(l>r)return;
int mid=(l+r)>>1;
x=++tot;
ch[x][0]=ch[x][1]=0;
fa[x]=f;
pw[x]=arr[mid];
sz[x]=1,rev[x]=0;
build(ch[x][0],x,arr,l,mid-1);
build(ch[x][1],x,arr,mid+1,r);
push_up(x);
}
int get_Kth(int K)
{
K++;
int x=rt;
while(push_down(x),K!=sz[ch[x][0]]+1)
{
if(K<=sz[ch[x][0]])x=ch[x][0];
else K-=sz[ch[x][0]]+1,x=ch[x][1];
}
splay(x,0);
return x;
}
void insert(int pos,char *str,int n)
{
int l=get_Kth(pos),r=get_Kth(pos+1);
splay(l,0),splay(r,l);
build(ch[r][0],r,str,0,n-1);
splay(r,0);
}
void flip(int l,int r)
{
l=get_Kth(l-1),r=get_Kth(r+1);
splay(l,0),splay(r,l);
reved(ch[r][0]);
}
void erase(int l,int r)
{
l=get_Kth(l-1),r=get_Kth(r+1);
splay(l,0),splay(r,l);
ch[r][0]=0;
splay(r,0);
}
}SP;
int n,mouse;
char str[N];
int main()
{
SP.init();
mouse=0;
scanf("%d",&n);
while(n--)
{
int x;
scanf("%s",str);
if(str[0]=='M')
{
scanf("%d",&x);
mouse=x;
}
else if(str[0]=='I')
{
scanf("%d",&x);getchar();
FOR(i,0,x-1)str[i]=getchar();
str[x]='\0';
SP.insert(mouse,str,x);
}
else if(str[0]=='D')
{
scanf("%d",&x);
SP.erase(mouse+1,mouse+x);
}
else if(str[0]=='R')
{
scanf("%d",&x);
SP.flip(mouse+1,mouse+x);
}
else if(str[0]=='G')
printf("%c\n",SP[mouse+1]);
else if(str[0]=='P')mouse--;
else if(str[0]=='N')mouse++;
}
return 0;
}
BZOJ 1269 文本编辑器editor(伸展树)的更多相关文章
- [AHOI 2006][BZOJ 1269]文本编辑器editor
好吧,我承认这是我用来刷随笔数的喵~ 这是一道 splay 裸题,但还是有想本傻 X 一样根本不会写 splay 的,于是乎又用 treap 水过了 splay 的常数我还是知道的,所以真是不知道那些 ...
- BZOJ 1269 文本编辑器 Splay
题目大意:维护一个文本编辑器,支持下列操作: 1.将光标移动到某一位置 2.在光标后插入一段字符串 3.删除光标后的一段字符 4.翻转光标后的一段字符 5.输出光标后的一个字符 6.光标-- 7.光标 ...
- 【BZOJ】【1269】【AHOI2006】文本编辑器editor
Splay Splay序列维护的模板题了……为了便于处理边界情况,我们可以先插入两个空格当作最左端和最右端,然后……其实本题主要考察的就是Build.splay和Findkth这三个操作,我们可以实现 ...
- BZOJ 1269: [AHOI2006]文本编辑器editor( splay )
splay..( BZOJ 1507 题目基本相同..双倍经验 ) ------------------------------------------------------------------ ...
- BZOJ 1269: [AHOI2006]文本编辑器editor (splay tree)
1269: [AHOI2006]文本编辑器editor Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1213 Solved: 454[Submit ...
- AHOI2006文本编辑器editor
1269: [AHOI2006]文本编辑器editor Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1885 Solved: 683[Submit ...
- BZOJ_1269&&1507_[AHOI2006]文本编辑器editor&&[NOI2003]Editor
BZOJ_1269&&1507_[AHOI2006]文本编辑器editor&&[NOI2003]Editor 题意: 分析: splay模拟即可 注意1507的读入格式 ...
- BZOJ1269 [AHOI2006]文本编辑器editor 【82行splay】
1269: [AHOI2006]文本编辑器editor Time Limit: 10 Sec Memory Limit: 162 MB Submit: 4633 Solved: 1782 [Sub ...
- [bzoj1269]文本编辑器editor [bzoj1500]维修数列
1269: [AHOI2006]文本编辑器editor Time Limit: 10 Sec Memory Limit: 162 MB Submit: 2540 Solved: 923 [Submit ...
随机推荐
- Java程序员必会英语单词
Complie: 编译 line: 行 variable: 变量 parameter: 参数 defaul: 默认 access: 访问 operation: 操作运算 member-variabl ...
- JavaScript基础知识(数组的方法)
数组的方法(15个) 对象数据类型: 数组成员有一个与之对应的索引 length : 代表数组成员的个数: 操作改变数组一些方法:这些数组的方法都是内置的: // 1. 方法作用: // 2. 方法的 ...
- AIX 网络设置
AIX使用命令修改网卡IP地址,永久生效 比如修改en0的ip地址.chdev -l en0 -a netaddr=192.168.1.100 -a netmask=255.255.255.0 -a ...
- Gym 101873D - Pants On Fire - [warshall算法求传递闭包]
题目链接:http://codeforces.com/gym/101873/problem/D 题意: 给出 $n$ 个事实,表述为 "XXX are worse than YYY" ...
- Luogu 1068 - 分数线划定 - [快速排序]
题目链接:https://www.luogu.org/problemnew/show/P1068 题目描述世博会志愿者的选拔工作正在 A 市如火如荼的进行.为了选拔最合适的人才,A 市对所有报名的选手 ...
- SQL执行计划解读
声明 5.6中desc看不到show warnings,也看不到filtered列 5.7的desc等于5.6的desc extended,这样可以看show warnings,5.6中filtere ...
- Delphi 中的 XMLDocument 类详解(10) - 判断节点类型: 支节点、叶节点、文本节点、空节点
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, For ...
- linux 逆向映射机制浅析
2017-05-20 聚会回来一如既往的看了会羽毛球比赛,然后想到前几天和朋友讨论的逆向映射的问题,还是简要总结下,免得以后再忘记了!可是当我添加时间……这就有点尴尬了……520还在写技术博客…… 闲 ...
- http 你造吗?
HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式,适用于分布式超媒体信息系统.它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展.目前在WWW中使用的是HTTP/1.0的第 ...
- 使用spring data solr 实现搜索关键字高亮显示
后端实现: @Service public class ItemSearchServiceImpl implements ItemSearchService { @Autowired private ...