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

比赛

题目

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


分析

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


代码

#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;
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值