P1383 高级打字机(可持久化线段树)

传送门:高级打字机
看到题目中的撤销操作就可以想到历史版本,不难想到使用可持久化线段树。
难点在于如何维护撤销操作,容易想到可以利用 \(root\) 数组。我们直接把 \(x\) 个操作前的 \(root\) 赋给当前的 \(root\),就可以保证撤销操作。
需要注意的是询问操作需要过滤掉,以保证撤销后的信息可以覆盖之前的信息。

#include<bits/stdc++.h>
using namespace std;
const int N = 100000 + 10;
struct Seg
{
	int l,r;
	char val;
} tree[32*N];
int root[32*N],len[32*N];
int n,tot,cnt;
int build(int p,int l,int r)
{
	p = ++tot;
	if(l == r) return p;
	int mid = (l + r) >> 1;
	tree[p].l = build(tree[p].l,l,mid);
	tree[p].r = build(tree[p].r,mid + 1,r);
	return p;
}
int create(int p)
{
	tot++;
	tree[tot] = tree[p];
	return tot;
}
int modify(int p,int l,int r,int x,char v)
{
	p = create(p);
	if(l == r)
	{
		tree[p].val = v;
		return p;
	}
	int mid = (l + r) >> 1;
	if(x <= mid) tree[p].l = modify(tree[p].l,l,mid,x,v);
	else tree[p].r = modify(tree[p].r,mid + 1,r,x,v);
	return p;
}
char query(int p,int l,int r,int x)
{
	if(l == r) return tree[p].val;
	int mid = (l + r) >> 1;
	if(x <= mid) return query(tree[p].l,l,mid,x);
	else return query(tree[p].r,mid + 1,r,x);
}
signed main()
{
	scanf("%d",&n);
	root[0] = build(0,1,n);
	for(int i = 1;i <= n;i++)
	{
		char opt;
		cin >> opt;
		if(opt == 'T')
		{
			cnt++;
			char v;
			cin >> v;
			len[cnt] = len[cnt-1] + 1;
			root[cnt] = modify(root[cnt-1],1,n,len[cnt],v);
		}
		else if(opt == 'U')
		{
			int x;
			scanf("%d",&x);
			cnt++;
			root[cnt] = root[cnt-x-1];
			len[cnt] = len[cnt-x-1];
		}
		else if(opt == 'Q')
		{
			int x;
			scanf("%d",&x);
			//cout << x << endl;
			cout << query(root[cnt],1,n,x) << endl;
			//root[i] = root[i-1];
			//len[i] = len[i-1];
		}
	}
	return 0;
}
posted @ 2021-12-19 12:10  一程山雪  阅读(61)  评论(0)    收藏  举报