Problem 1 高级打字机(type.cpp/c/pas)
【题目描述】
早苗入手了最新的高级打字机。最新款自然有着与以往不同的功能,那就是它具备撤销功能,厉害吧。
请为这种高级打字机设计一个程序,支持如下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挑战>
必须使用在线算法完成该题。
好像还是IOI2012的
其实这题很水,100分其实一个线段树就好了,每个节点记录一下区间中有多少字母,然后查询就像权值线段树查第k大那样就好了。
然后200分就如题了,可持久化线段树再加上前面的做法。
这题真的一眼秒。
代码:(是hzwer代码时间的1/4)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define ll long long
using namespace std;
inline int read(){
int x=0;char ch=' ';int f=1;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
struct node{
int ls,rs,size;
char c;
}t[100001*30];
int m,n,tot;
int root[100001];
inline void build(int &now,int l,int r){
now=++tot;
if(l==r)return;
int mid=(l+r)>>1;
build(t[now].ls,l,mid);
build(t[now].rs,mid+1,r);
}
inline void insert(int &now,int pre,int l,int r,int pos,char c){
now=++tot;
t[now]=t[pre];
if(c>='a'&&c<='z')t[now].size++;
if(l==r){t[now].c=c;return;}
int mid=(l+r)>>1;
if(pos<=mid)insert(t[now].ls,t[pre].ls,l,mid,pos,c);
else insert(t[now].rs,t[pre].rs,mid+1,r,pos,c);
}
inline char query(int now,int l,int r,int x){
if(l==r)return t[now].c;
int mid=(l+r)>>1;
int lsize=t[t[now].ls].size;
if(x<=lsize)return query(t[now].ls,l,mid,x);
else return query(t[now].rs,mid+1,r,x-lsize);
}
int main(){
freopen("type.in","r",stdin);
freopen("type.out","w",stdout);
m=read();
for(int i=1;i<=m;i++){
char ch[3];
int x;
scanf("%s",ch);
if(ch[0]=='T'){
scanf("%s",ch);
n++;
insert(root[n],root[n-1],1,m,n,ch[0]);
}
else if(ch[0]=='U'){
x=read();
n++;
insert(root[n],root[n-x-1],1,m,m,48);
}
else{
x=read();
ch[0]=query(root[n],1,m,x);
printf("%s\n",ch);
}
}
return 0;
}