【JZOJ3794】【洛谷P1383】高级打字机【主席树】

题目大意:

题目链接:

洛谷:https://www.luogu.org/problemnew/show/P1383
JZOJ:https://jzoj.net/senior/#main/show/3794

要求一种数据结构满足这样的操作:

  1. T xT x:在文章末尾打下一个小写字母xx
  2. U xU x:撤销最后的xx次修改操作。
  3. Q xQ x:询问当前文章中第xx个字母并输出。

思路:

吐槽

我是不是可以去IOI了XD

这道题很显然是用主席树维护这个序列。干脆直接开一个长度为10510^5的主席树。每一个叶子节点储存这个节点的字母。这样虽然会大大增加空间但是不会MLE啊XD
主席树的基础题吧。对于每一个TT操作,新开一个主席树。对于每一个UU操作,把第cntx1cnt-x-1个主席树抠到第cntcnt个主席树中。对于每一个QQ操作,直接输出第cntcnt个主席树的第xx位即可。
时间复杂度O(T log n)O(T log n),空间复杂度O(n log n)O(n log n)


代码:

#include <cstdio>
#include <iostream>
using namespace std;

const int N=100010;
int T,tot,sum[N],cnt,x,root[N];
char ch;

struct Tree
{
	int ls,rs;
	char c;
}tree[N*20];

int build(int l,int r)
{
	int p=++tot;
	if (l<r)
	{
		int mid=(l+r)/2;
		tree[p].ls=build(l,mid);
		tree[p].rs=build(mid+1,r);
	}
	return p;
}

int add(int now,int l,int r,int k,char val)  //插入
{
	int p=++tot;
	tree[p]=tree[now];
	if (l==r) tree[p].c=val;
	else
	{
		int mid=(l+r)/2;
		if (k<=mid) tree[p].ls=add(tree[now].ls,l,mid,k,val);
			else tree[p].rs=add(tree[now].rs,mid+1,r,k,val);
	}
	return p;
}

char ask(int x,int l,int r,int k)  //询问
{
	if (l==k&&r==k) return tree[x].c;  //找到
	int mid=(l+r)/2;
	if (k<=mid) return ask(tree[x].ls,l,mid,k);
		else return ask(tree[x].rs,mid+1,r,k);
}

int main()
{
	scanf("%d",&T);
	root[0]=build(1,N);
	while (T--)
	{
		cin>>ch;
		if (ch=='T')
		{
			cin>>ch;
			cnt++;
			sum[cnt]=sum[cnt-1]+1;
			root[cnt]=add(root[cnt-1],1,N,sum[cnt],ch);
		}
		else if (ch=='U')
		{
			scanf("%d",&x);
			cnt++;
			root[cnt]=root[cnt-x-1];
			sum[cnt]=sum[cnt-x-1];
		}
		else
		{
			scanf("%d",&x);
			putchar(ask(root[cnt],1,N,x));
			putchar(10);
		}
	}
	return 0;
}

再次吐槽
JZOJ这道题可以用Pascal的滚动数组+ansistring过掉…

原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998286.html