【高级打字机-给你一个主席树】

高级打字机

【题目描述】

早苗入手了最新的高级打字机。最新款自然有着与以往不同的功能,那就是它具备撤销功能,厉害吧。请为这种高级打字机设计一个程序,支持如下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了,那么就轻松打破了我们想要仅仅通过维护栈来获得历史状态的梦想。但碎梦是美妙的,至少它启示我们要想一种更美妙的方法来快速、简洁地维护历史状态。

     这道题的特点是:每一次操作都是在原来的基础上进行的,可以理解为状态具有连续性,只不过联结的纽带不是简单地前前后后。

     主席树具有高效维护历史版本的特性。总结来说,这种不寻常的线段树结构的构建规则是:“以前有的我直接取,我独有的就在建立新节点”

image

(图示为线段树合成主席树,作用在于节省空间,快速查询。不过实际上主席树的构建还有许多细节)

·这一题仅有主席树的插入操作和单点查询操作,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
如果生命只是一场碎梦 我为什么还在追逐,
如果人们看到我的背影 还会不会为这个傻瓜而感动.————汪峰《碎梦》
原文地址:https://www.cnblogs.com/Paul-Guderian/p/6849209.html