1507: [NOI2003]Editor
Time Limit: 5 Sec Memory Limit: 162 MBSubmit: 2083 Solved: 800
[ Submit][ Status]
Description

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
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
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)!!