【BZOJ 1269】 [AHOI2006]文本编辑器editor

1269: [AHOI2006]文本编辑器editor

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 2017   Solved: 733
[ Submit][ Status]

Description

这些日子,可可不和卡卡一起玩了,原来可可正废寝忘食的想做一个简单而高效的文本编辑器。你能帮助他吗?为了明确任务目标,可可对“文本编辑器”做了一个抽象的定义:   文本:由0个或多个字符构成的序列。这些字符的ASCII码在闭区间[32, 126]内,也就是说,这些字符均为可见字符或空格。光标:在一段文本中用于指示位置的标记,可以位于文本的第一个字符之前,文本的最后一个字符之后或文本的某两个相邻字符之间。文本编辑器:为一个可以对一段文本和该文本中的一个光标进行如下七条操作的程序。如果这段文本为空,我们就说这个文本编辑器是空的。 编写一个程序: 建立一个空的文本编辑器。 从输入文件中读入一些操作指令并执行。 对所有执行过的GET操作,将指定的内容写入输出文件。

Input

输入文件中第一行是指令条数N,以下是需要执行的N个操作。除了回车符之外,输入文件的所有字符的ASCII码都在闭区间[32, 126]内。且行尾没有空格。

Output

依次对应输入文件中每条GET指令的输出,不得有任何多余的字符。

Sample Input

10
Insert 13
Balanced eert
Move 2
Delete 5
Next
Insert 7
editor
Move 0
Get
Move 11
Rotate 4
Get

Sample Output

B
t

HINT

对输入数据我们有如下假定: MOVE操作不超过50 000个,INSERT、DELETE和ROTATE操作作的总个数不超过6 000,GET操作不超过20 000个,PREV和NEXT操作的总个数不超过20 000。 所有INSERT插入的字符数之和不超过2M(1M=1 024*1 024)。 DELETE操作、ROTATE操作和GET操作执行时光标后必然有足够的字符。MOVE、PREV、NEXT操作不会把光标移动到非法位置。 输入文件没有错误。

Source


splay操作的模板题。


这道题要求维护一个数列,支持一系列操作(光标的位置用一个pos记录即可):

Move(k):直接把pos赋值为k+1(为什么是k+1,后面再说)


Insert(n,str):先把pos旋转到根节点,再把pos+1旋到根结点的儿子,然后直接在pos+1的左儿子插入str即可


Delete(n):先把pos旋转到根节点,再把pos+n+1旋到根结点的儿子,然后删除pos+k+1的左儿子


Rotate(n):先把pos旋转到根节点,再把pos+n+1旋到根结点的儿子,然后把pos+k+1的左儿子的rev异或


Get():先把pos旋转到根节点,输出根节点右儿子中最小的那个结点所存储的字符


Prev():直接把pos--


Next():直接把pos++


为什么要在Move(k)把pos赋值为k+1,而不是k?

因为在后面的插入删除反转的操作中,要将当前位置的前一位转到根结点,如果当前位置是第一位就没有可转的了。所以在最开始的时候,先插入两个' ',表示开头和结尾,就不会出错了。


#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <cmath>
#define maxn 1024*1024*3
using namespace std;
int tot=0,pos;
struct splay
{
	int rev,l,r,fa,size;
	char data;
}a[maxn];
char str[maxn];
int root=0,q,k;
void Push_up(int x)
{
	a[x].size=a[a[x].l].size+a[a[x].r].size+1;
}
void Push_down(int x)
{
	if (a[x].rev)
	{
		swap(a[x].l,a[x].r);
		a[x].rev=0;
		a[a[x].l].rev^=1;
		a[a[x].r].rev^=1;
	}
}
void zig(int x)
{
	int y=a[x].fa;
	int z=a[y].fa;
	Push_down(y);
	Push_down(x);
	a[x].fa=z,a[y].fa=x;
	a[y].l=a[x].r,a[a[x].r].fa=y,a[x].r=y;
	if (a[z].l==y) a[z].l=x;
	else a[z].r=x;
	Push_up(y);
}
void zag(int x)
{
	int y=a[x].fa;
	int z=a[y].fa;
	Push_down(y);
	Push_down(x);
	a[x].fa=z,a[y].fa=x;
	a[y].r=a[x].l,a[a[x].l].fa=y,a[x].l=y;
	if (a[z].l==y) a[z].l=x;
	else a[z].r=x;
	Push_up(y);
}
void splay(int x,int s)
{
	Push_down(x);
	while (a[x].fa!=s)
	{
		int y=a[x].fa;
		int z=a[y].fa;
		if (a[y].fa==s)
		{
			if (x==a[y].l) zig(x);
			else zag(x);
			break;
		}
		if (y==a[z].l)
		{
			if (x==a[y].l) zig(y),zig(x);
			else zag(x),zig(x);
		}
		else
		{
			if (x==a[y].r) zag(y),zag(x);
			else zig(x),zag(x);
		}
	}
	Push_up(x);
	if (s==0) root=x;
}
int Getmin(int x)
{
	Push_down(x);
	while (a[x].l)
	{
		x=a[x].l;
		Push_down(x);
	}
	return x;
}
void New_Node(int &x,int fa,char key)
{
	x=++tot;
	a[x].fa=fa;
	a[x].rev=a[x].l=a[x].r=0;
	a[x].data=key;
}
void Build(int &x,int fa,int l,int r,char *str)
{
	if (l>r) return;
	int m=(l+r)>>1;
	New_Node(x,fa,str[m]);
	Build(a[x].l,x,l,m-1,str);
	Build(a[x].r,x,m+1,r,str);
	Push_up(x);
}
int Findkth(int x,int k)
{
	Push_down(x);
	int s=a[a[x].l].size;
	if (s+1==k) return x;
	if (s>=k) return Findkth(a[x].l,k);
	return Findkth(a[x].r,k-1-s);
}
void Insert(char *str)
{
	int x=Findkth(root,pos);
	splay(x,0);
	x=Getmin(a[root].r);
	splay(x,root);
	Build(a[a[root].r].l,a[root].r,0,strlen(str)-1,str);
}
void Delet(int k)
{
	int x=Findkth(root,pos);
	splay(x,0);
	int y=Findkth(root,pos+k+1);
	splay(y,root);
	a[a[y].l].fa=0;
	a[y].l=0;
	Push_up(a[root].r);
	Push_up(root);
}
void Reserve(int k)
{
	int x=Findkth(root,pos);
	splay(x,0);
	int y=Findkth(root,pos+k+1);
	splay(y,root);
	a[a[y].l].rev^=1;
}
void Print()
{
	int x=Findkth(root,pos);
	splay(x,0);
	int y=Getmin(a[root].r);
	printf("%c\n",a[y].data);
}
int main()
{
	scanf("%d",&q);
	pos=1;
	for (int i=0;i<=1;i++)
	  str[i]=' ';
        Build(root,0,0,1,str);
	while (q--)
	{
		scanf("%s",str);
		if (str[0]=='I')
		{
			scanf("%d",&k);
			getchar();
			gets(str);
			Insert(str);
		}
		else if (str[0]=='M')
		{
			scanf("%d",&k);
			pos=k+1;
		}
		else if (str[0]=='D')
		{
			scanf("%d",&k);
			Delet(k);
		}
		else if (str[0]=='R')
		{
			scanf("%d",&k);
			Reserve(k);
		}
		else if (str[0]=='G')
		{
			Print();
		}
		else if (str[0]=='P')
			pos--;
		else 
			pos++;
	}
	return 0;
}




小结:

1.一开始提交一直RE,结果是字符数组只开了100!!!以后要注意检查空间是否够用。


2.splay可以维护有序的数列,而数列分为两种:以权值为关键字和以位置为关键字,本题是以位置为关键字


3.splay(x,s)的时候,注意在旋转前先把标记下传,至于上传标记:zigzag中只要上传y的标记,而x的标记在旋转标记后再上传,这样不仅可以保证正确性,而且减小代码的常数。

附上Crash《运用伸展树解决数列维护问题》的解释:




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值