#trie,倍增,主席树#JZOJ 3794 高级打字机

比赛

题目

T x:在文章末尾打下一个小写字母x。(type操作)
U x:撤销最后的x次修改操作。(Undo操作)(注意Query操作并不算修改操作)
Q x:询问当前文章中第x个字母并输出。(Query操作)文章一开始可以视为空串。


分析

可持久化数据结构特别多,在此不多说,这种方法是Trie+倍增,bel表示版本,dep表示距离第一个版本的深度, f a [ i ] [ j ] 表示第 i 2 j + 1 的版本,倍增查询,当撤销时从第x个版本跳转到x-y-1个版本,输入字母就创建新的节点。时间复杂度 O n l o g n


代码

#include <cstdio>
#include <cctype>
#define gc getchar()
using namespace std;
int n,bel[100001],ne,nt,dep[100001],fa[100001][21]; char c[100001];
int in(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
int main(){
    n=in();
    while (n--){
        scanf("\n"); char cho=gc; gc;
        if (cho=='T'){
            bel[++ne]=++nt;//创建新的版本
            dep[nt]=dep[bel[ne-1]]+1;//深度
            fa[nt][0]=bel[ne-1];//第i-2^0+1个版本是它本身
            for (int i=0;fa[fa[nt][i]][i];i++)
            fa[nt][i+1]=fa[fa[nt][i]][i];//倍增
            c[nt]=gc;//加入字母
        }
        if (cho=='U') bel[++ne]=bel[ne-in()-1];//指向前面的版本
        if (cho=='Q'){
            int p=bel[ne];
            for (int x=dep[p]-in(),i=0;x;x>>=1,i++)//倍增
            if (x&1) p=fa[p][i];//当找到当前最早的祖先时继续找祖先
            putchar(c[p]); putchar('\n');
        }
    }
    return 0;
}

Another分析

可以用主席树,每次加入logn的点,时间复杂度O(nlogn)


代码

#include <cstdio>
#include <cctype>
#define gc getchar()
#define N 100001
using namespace std;
int n,len[N],rt[N],k,tot,x;
struct cmtree{
    int lson[N*9],rson[N*9]; char val[N*9];
    void add(int &t,int f,int l,int r,int pos,char c){
        if (!t) t=++tot;//动态开点
        if (l==r) val[t]=c;//叶子节点
        else{
            int mid=(l+r)>>1;
            if (pos<=mid) rson[t]=rson[f],add(lson[t],lson[f],l,mid,pos,c);
            else lson[t]=lson[f],add(rson[t],rson[f],mid+1,r,pos,c); 
        }
    }   
    char query(int t,int l,int r,int pos){
        if (l==r) return val[t];
        else{
            int mid=(l+r)>>1;
            if (pos<=mid) return query(lson[t],l,mid,pos);
            else return query(rson[t],mid+1,r,pos);
        }
    }
}tree;
int in(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
int main(){
    n=in();
    while (n--){
        scanf("\n"); char cho=gc; gc;
        if (cho=='T'){
            len[++k]=len[k-1]+1; char c=gc;
            tree.add(rt[k],rt[k-1],1,N,len[k],c);//建树
        }
        if (cho=='U') {x=in(); len[++k]=len[k-x-1]; rt[k]=rt[k-x-1];}//跳到前面的版本
        if (cho=='Q') putchar(tree.query(rt[k],1,N,in())),putchar('\n');//输出答案
    }
    return 0;
}
}

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/80990463