2702 高级打字机

2702 高级打字机_第1张图片

因为Undo操作只能撤销Type操作,所以Undo x 实际上就是删除文章末尾x个字母。用一个栈即可解决(每个字母最多进出一次)。


这种情况下只需要设计一个合理的数据结构依次执行操作即可。

版本树:Undo x撤销最近的x次修改操作,实际上就是当前版本还原为x次操作前的版本,换句话说,版本i = 版本i-x-1。

2702 高级打字机_第2张图片

如图所示,所有版本呈树状排列,版本0为根。
读入所有操作并建树,对这颗版本树按欧拉序求出所有版本。上图中就是按0->1->4…4->1->0->2->3->2->0的顺序遍历,同样使用栈就能计算出所有的版本,然后在对应的版本上解决询问即可。
到此,就得到了时空复杂度均为O(n)的离线算法。
能解决这类题目的条件是:


1.允许使用离线算法,进而求出版本树,并允许把询问挂到树的节点上。
2.所有操作都是可逆的。只有所有操作都是可逆的,才能按欧拉序依次求出各版本。如本题的Type操作的逆操作就是弹出栈顶,Undo操作则根本不需要修改(Undo前后2个版本相同)。

#include
using namespace std;
const int R=1e5,N=(R+1)*20;
int n,m,now,sz,root[R+1],ls[N],rs[N],len[N];
char s[N];
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void insert(int &k,int last,int l,int r,int pos,int c){
    k=++sz;
    if(l==r){s[k]=c;return ;}
    ls[k]=ls[last];
    rs[k]=rs[last];
    int mid=l+r>>1;
    if(pos<=mid) insert(ls[k],ls[last],l,mid,pos,c);
    else insert(rs[k],rs[last],mid+1,r,pos,c);
}
void query(int &k,int last,int l,int r,int pos){
    if(l==r){putchar(s[k]);putchar('\n');return ;}
    int mid=l+r>>1;
    if(pos<=mid) query(ls[k],ls[last],l,mid,pos);
    else query(rs[k],rs[last],mid+1,r,pos);
}
int main(){
    n=read();
    for(int i=1,x;i<=n;i++){
        char op=0,ch=0;
        for(;op<'A'||op>'Z';op=getchar());
        if(op=='T'){
            for(;ch<'a'||ch>'z';ch=getchar());
            now++;
            len[now]=len[now-1]+1;
            insert(root[now],root[now-1],1,R,len[now],ch);
        }
        else if(op=='U'){
            x=read();
            now++;
            root[now]=root[now-x-1];
            len[now]=len[now-x-1];
        }
        else x=read(),query(root[now],root[now-1],1,R,x);
    }
    return 0;
}

你可能感兴趣的:(算法,java,数据结构)