高级打字机
【题目描述】
早苗入手了最新的高级打字机。最新款自然有着与以往不同的功能,那就是它具备撤销功能,厉害吧。请为这种高级打字机设计一个程序,支持如下3种操作:
1.T x:在文章末尾打下一个小写字母x。(type操作)
2.U x:撤销最后的x次修改操作。(Undo操作)(注意Query操作并不算修改操作)
3.Q x:询问当前文章中第x个字母并输出。(Query操作)文章一开始可以视为空串。
【输入格式】
第1行:一个整数n,表示操作数量。以下n行,每行一个命令。保证输入的命令合法。
【输出格式】
每行输出一个字母,表示Query操作的答案。
【样例输入】
7
T a
T b
T c
Q 2
U 2
T c
Q 2
【样例输出】
b
c
【数据范围】
对于40%的数据 n<=200;
对于100%的数据 n<=100000;保证Undo操作不会撤销Undo操作。
<高级挑战>对于200%的数据 n<=100000;Undo操作可以撤销Undo操作。
<IOI挑战>必须使用在线算法完成该题。
·分析
如果undo操作把别的undo操作给undo了,那么就轻松打破了我们想要仅仅通过维护栈来获得历史状态的梦想。但碎梦是美妙的,至少它启示我们要想一种更美妙的方法来快速、简洁地维护历史状态。
这道题的特点是:每一次操作都是在原来的基础上进行的,可以理解为状态具有连续性,只不过联结的纽带不是简单地前前后后。
主席树具有高效维护历史版本的特性。总结来说,这种不寻常的线段树结构的构建规则是:“以前有的我直接取,我独有的就在建立新节点”
(图示为线段树合成主席树,作用在于节省空间,快速查询。不过实际上主席树的构建还有许多细节)
·这一题仅有主席树的插入操作和单点查询操作,wow!
1 #include<stdio.h> 2 #include<cstring> 3 #define go(i,a,b) for(int i=a;i<=b;i++) 4 const int N=100003;struct Chair_Man_Tree{int l,r,word;}t[N*10]; 5 int q,Root[N],sz,rig[N],mid; 6 void insert(int& u,int L,int R,int pos,char w) 7 { 8 t[++sz]=t[u];u=sz;if(L==R){t[u].word=w;return;}mid=L+R>>1; 9 pos>mid?insert(t[u].r,mid+1,R,pos,w):insert(t[u].l,L,mid,pos,w); 10 } 11 void find(int u,int L,int R,int pos) 12 { 13 if(L==R){printf("%c ",t[u].word);return;}mid=L+R>>1; 14 pos>mid?find(t[u].r,mid+1,R,pos):find(t[u].l,L,mid,pos); 15 } 16 int main() 17 { 18 scanf("%d",&q);int now=0,i=0;go(I,1,q) 19 { 20 char c1,c2;int j;scanf(" %c",&c1); 21 if(c1=='T')i++,scanf(" %c",&c2),Root[i]=Root[i-1],insert(Root[i],1,N,++now,c2),rig[i]=now; 22 if(c1=='U')i++,scanf("%d",&j),j=i-j-1,Root[i]=Root[j],now=rig[i]=rig[j]; 23 if(c1=='Q')scanf("%d",&j),find(Root[i],1,N,j); 24 } 25 return 0; 26 }//Paul_Guderian
如果生命只是一场碎梦 我为什么还在追逐,
如果人们看到我的背影 还会不会为这个傻瓜而感动.————汪峰《碎梦》