【BZOJ 1507】[NOI2003]Editor

来源:互联网 发布:以色列 巴勒斯坦 知乎 编辑:程序博客网 时间:2024/06/03 20:58

1507: [NOI2003]Editor

Time Limit: 5 Sec  Memory Limit: 162 MB
Submit: 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

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*3using 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)!!

0 0
原创粉丝点击