「Luogu P6166 [IOI2012]scrivener」
题目大意
给出一个空字符串支持在末尾加上一个字符,查询某个位置的字符,回到 \(k\) 个操作前.
分析
显然的可持久化数组,可以通过可持久化线段树实现,为了方便处理,开始的区间是 \(1\) 到 \(1000000\) 开始,这样保证不会出问题,加上一个数就直接单点修改,查询也只要直接单点查询,对于每次修改对于修改的区间部分产生一个新的版本,其他仍然用之前版本的部分.
代码
#include<bits/stdc++.h>
#define RAP(i,first,last) for(int i=first;i<=last;++i)
#define LSON tree[now].lson
#define RSON tree[now].rson
#define MIDDLE ((left+right)>>1)
#define LEFT LSON,left,MIDDLE
#define RIGHT RSON,MIDDLE+1,right
#define NOW now_left,now_right
#define NEW_LSON tree[new_tree].lson
#define NEW_RSON tree[new_tree].rson
using namespace std;
const int maxM=2e6+7;
int N=1e6/*从1e6开始查询*/,M;
struct Tree
{
int sum,lson,rson;
}tree[maxM*32];
int cnt=0;
void UpData(int &new_tree,int num,int ch,int now,int left=1,int right=N)//标准可持久化线段树
{
if(num<left||right<num)//如果不会被修改就直接用原来的内容
{
new_tree=now;
return;
}
new_tree=++cnt;//否则产生一个新的点
if(left==right)//到叶节点就直接单点修改
{
tree[new_tree].sum=ch;
return;
}
UpData(NEW_LSON,num,ch,LEFT);
UpData(NEW_RSON,num,ch,RIGHT);
}
int Query(int k,int now,int left=1,int right=N)//单点查询
{
if(k<left||right<k)return 0;
if(left==right)//叶节点直接返回
{
return tree[now].sum;
}
return Query(k,LEFT)+Query(k,RIGHT);
}
int root[maxM];//记录每次修改的线段树的根节点编号
int len[maxM];//记录每个版本的数组长度
int main()
{
cin>>M;
char ch;
int x;
char add;
int tot=0;//记录当前修改操作个数
RAP(i,1,M)
{
cin>>ch;
if(ch=='T')//修改部分
{
++tot;
cin>>add;
len[tot]=len[tot-1]+1;
UpData(root[tot],len[tot],add,root[tot-1]);
}
if(ch=='U')//回到x个版本前,就直接改变线段树的根节点就好了
{
++tot;
cin>>x;
root[tot]=root[tot-x-1];
len[tot]=len[tot-x-1];
}
if(ch=='P')//单点查询
{
cin>>x;
printf("%c\n",Query(x+1,root[tot]));
}
}
return 0;
}