Time Limit: 10000MS | Memory Limit: 165888KB | 64bit IO Format: %lld & %llu |
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操作不会把光标移动到非法位置。? 输入文件没有错误。
分析:Splay的基本操作,对于move光标的操作,用一个pos指针来模拟就行了;对于插入操作,先进行伸展操作将pos所在位置移到根节点的位置,将pos+1的位置移到根节点右子树的位置,然后在其左子树插入字符串就行了,对于删除和翻转也是相同的做法
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; #define Key_value ch[ch[root][1]][0] const int MAXN = 2*1024*1024+10; const int INF = 0X3F3F3F3F; int fa[MAXN],ch[MAXN][2],sz[MAXN],rev[MAXN]; char key[MAXN]; int root,tot1; int s[MAXN],tot2;//内存池、内存池容量 int pos; char str[MAXN]; int n; void NewNode(int &r,int pre,char k) { if(tot2) r=s[tot2--]; else r=++tot1; ch[r][0]=ch[r][1]=0; fa[r]=pre; sz[r]=1; rev[r]=0; key[r]=k; } void Update_Rev(int r) { if(r==0) return; swap(ch[r][0],ch[r][1]); rev[r]^=1; } void Push_Up(int r) { sz[r]=sz[ch[r][0]]+sz[ch[r][1]]+1; } void Push_Down(int r) { if(rev[r]) { Update_Rev(ch[r][0]); Update_Rev(ch[r][1]); rev[r]=0; } } void Build(int &x,int l,int r,int pre) { if(l>r) return; int mid=(l+r)/2; NewNode(x,pre,str[mid]); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); Push_Up(x); } void Init() { pos=0; root=tot1=tot2=0; ch[root][0]=ch[root][1]=sz[root]=rev[root]=fa[root]=0; NewNode(root,0,‘ ‘); NewNode(ch[root][1],root,‘ ‘); //Build(Key_value,1,n,ch[root][1]); Push_Up(ch[root][1]); Push_Up(root); } void Rotate(int x,int d) { int y=fa[x]; Push_Down(y); Push_Down(x); ch[y][!d]=ch[x][d]; fa[ch[x][d]]=y; if(fa[y]) ch[fa[y]][ch[fa[y]][1]==y]=x; fa[x]=fa[y]; ch[x][d]=y; fa[y]=x; Push_Up(y); } //伸展操作,将r调整到goal下面 void Splay(int r,int goal) { Push_Down(r); while(fa[r]!=goal) { if(fa[fa[r]]==goal) { //这题有反转操作,需要先push_down,在判断左右孩子 Push_Down(fa[r]); Push_Down(r); Rotate(r,ch[fa[r]][0]==r); } else { //这题有反转操作,需要先push_down,在判断左右孩子 Push_Down(fa[fa[r]]); Push_Down(fa[r]); Push_Down(r); int y=fa[r]; int d=(ch[fa[y]][0]==y); //两个方向不同,则先左旋再右旋 if(ch[y][d]==r) { Rotate(r,!d); Rotate(r,d); } //两个方向相同,相同方向连续两次 else { Rotate(y,d); Rotate(r,d); } } } Push_Up(r); if(goal==0) root=r; } int Get_Kth(int r,int k) { Push_Down(r); int t=sz[ch[r][0]]+1; if(t==k)return r; if(t>k)return Get_Kth(ch[r][0],k); else return Get_Kth(ch[r][1],k-t); } void Erase(int r) { if(r) { s[++tot2]=r; Erase(ch[r][0]); Erase(ch[r][1]); } } void update_int(int len) { Splay(Get_Kth(root,pos+1),0); Splay(Get_Kth(root,pos+2),root); Build(Key_value,0,len-1,ch[root][1]); Push_Up(ch[root][1]); Push_Up(root); } void update_del(int len) { Splay(Get_Kth(root,pos+1),0); Splay(Get_Kth(root,pos+len+2),root); Erase(Key_value); fa[Key_value]=0; Key_value=0; Push_Up(ch[root][1]); Push_Up(root); } void update_rot(int len) { Splay(Get_Kth(root,pos+1),0); Splay(Get_Kth(root,pos+len+2),root); Update_Rev(Key_value); Push_Up(ch[root][1]); Push_Up(root); } int main() { int x; char op[20]; scanf("%d",&n); Init(); while(n--) { scanf("%s",op); if(strcmp(op,"Insert")==0) { scanf("%d",&x); getchar(); gets(str); //cout<<str<<endl; update_int(x); } else if(strcmp(op,"Delete")==0) { scanf("%d",&x); update_del(x); } else if(strcmp(op,"Rotate")==0) { scanf("%d",&x); update_rot(x); } else if(strcmp(op,"Get")==0) { printf("%c\n",key[Get_Kth(root,pos+2)]); } else if(strcmp(op,"Move")==0) { scanf("%d",&x); pos=x; } else if(strcmp(op,"Next")==0) pos++; else if(strcmp(op,"Prev")==0) pos--; } return 0; }