【BZOJ 1507】[NOI2003]Editor

1507: [NOI2003]Editor

Time Limit: 5 Sec   Memory Limit: 162 MB
Submit: 2083   Solved: 800
[ Submit][ Status]

Description

【BZOJ 1507】[NOI2003]Editor_第1张图片

Input

输入文件editor.in的第一行是指令条数t,以下是需要执行的t个操作。其中: 为了使输入文件便于阅读,Insert操作的字符串中可能会插入一些回车符,请忽略掉它们(如果难以理解这句话,可以参考样例)。 除了回车符之外,输入文件的所有字符的ASCII码都在闭区间[32, 126]内。且行尾没有空格。 这里我们有如下假定:  MOVE操作不超过50000个,INSERT和DELETE操作的总个数不超过4000,PREV和NEXT操作的总个数不超过200000。  所有INSERT插入的字符数之和不超过2M(1M=1024*1024),正确的输出文件长度不超过3M字节。  DELETE操作和GET操作执行时光标后必然有足够的字符。MOVE、PREV、NEXT操作必然不会试图把光标移动到非法位置。  输入文件没有错误。 对C++选手的提示:经测试,最大的测试数据使用fstream进行输入有可能会比使用stdio慢约1秒。

Output

输出文件editor.out的每行依次对应输入文件中每条GET指令的输出。

Sample Input

15
Insert 26
abcdefghijklmnop
qrstuv wxy
Move 16
Delete 11
Move 5
Insert 1
^
Next
Insert 1
_
Next
Next
Insert 4
.\/.
Get 4
Prev
Insert 1
^
Move 0
Get 22

Sample Output

.\/.
abcde^_^f.\/.ghijklmno

splay操作的裸题。(BZOJ1269的简化版)

这道题要求维护一个数列,支持一系列操作(光标的位置用一个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的左儿子

Get(n):先把pos旋转到根节点,递归输出n个数

Prev():直接把pos--

Next():直接把pos++


pos赋值为k+1的原因在BZOJ1269的题解中已经说过。


#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#define maxn 1024*1024*3
using namespace std;
int cnt,root,pos,tot=0,q,k;
struct splay
{
	int l,r,size,fa;
	char data;
}a[maxn];
char str[maxn];
void Push_up(int x)
{
	a[x].size=1+a[a[x].l].size+a[a[x].r].size;
}
void zig(int x)
{
	int y=a[x].fa;
	int z=a[y].fa;
	a[y].fa=x,a[x].fa=z;
	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;
	a[y].fa=x,a[x].fa=z;
	a[y].r=a[x].l,a[a[x].l].fa=y,a[x].l=y;
	if (y==a[z].l) a[z].l=x;
	else a[z].r=x;
	Push_up(y);
}
void splay(int x,int s)
{
	while (a[x].fa!=s)
	{
		int y=a[x].fa;
		int z=a[y].fa;
		if (z==s)
		{
			if (x==a[y].l) zig(x);
			else zag(x);
		}
		else
		{
			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 Findkth(int x,int k)
{
	int s=a[a[x].l].size;
	if (k==s+1) return x;
	if (k<=s) return Findkth(a[x].l,k);
	return Findkth(a[x].r,k-s-1);
}
int Getmin(int x)
{
	while (a[x].l)
		x=a[x].l;
	return x;
}
void New_Node(int &x,int fa,char c)
{
	x=++tot;
	a[x].fa=fa;
	a[x].l=a[x].r=a[x].size=0;
	a[x].data=c;
}
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);
}
void Insert(char *str,int l)
{
	int x=Findkth(root,pos);
	splay(x,0);
	x=Getmin(a[root].r);
	splay(x,root);
	Build(a[x].l,x,0,l-1,str);
}
void Delet(int k)
{
	int x=Findkth(root,pos);
	splay(x,0);
	x=Findkth(root,pos+k+1);
	splay(x,root);
	a[a[x].l].fa=0;
	a[x].l=0;
	Push_up(x);
	Push_up(root);
}
void dg(int x,int k)
{
	if (!a[x].r&&!a[x].l)
	{
		cnt++;
		if (cnt>k) return;
		printf("%c",a[x].data);
		return;
	}
	if (a[x].l) dg(a[x].l,k);
	cnt++;
	if (cnt>k) return;
	printf("%c",a[x].data);
	if (a[x].r) dg(a[x].r,k);
}
void Print(int k)
{
	int x=Findkth(root,pos);
	splay(x,0);
	cnt=0;
	dg(a[root].r,k);
	printf("\n");
}
int main()
{
    scanf("%d",&q);
	root=tot=0;
	pos=1;
	Build(root,0,0,1,"  ");
	while (q--)
	{
		scanf("%s",str);
		if (str[0]=='I')
		{
			scanf("%d",&k);
			getchar();
			for (int i=0;i<k;i++)
			{
				str[i]=getchar();
				if (str[i]=='\n') {i--;continue;}
			}
			Insert(str,k);
		}
		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]=='P')
			pos--;
		else if (str[0]=='N')
			pos++;
		else 
		{
			scanf("%d",&k);
			Print(k);
		}
	}
	return 0;
}



小结:

1.第一次交TLE了,因为在写Get的时候我直接找排在第i位的数(Findkth(i)),每一次复杂度为O(nlogn),有n次Get操作复杂度就会变成O(n^2logn)!!

你可能感兴趣的:(【BZOJ 1507】[NOI2003]Editor)