题目大意:
题目链接:
洛谷:https://www.luogu.org/problemnew/show/P1383
JZOJ:https://jzoj.net/senior/#main/show/3794
要求一种数据结构满足这样的操作:
- T x T\ x T x:在文章末尾打下一个小写字母 x x x。
- U x U\ x U x:撤销最后的 x x x次修改操作。
- Q x Q\ x Q x:询问当前文章中第 x x x个字母并输出。
思路:
吐槽
我是不是可以去IOI了XD
这道题很显然是用主席树维护这个序列。干脆直接开一个长度为
1
0
5
10^5
105的主席树。每一个叶子节点储存这个节点的字母。这样虽然会大大增加空间但是不会MLE啊XD
主席树的基础题吧。对于每一个
T
T
T操作,新开一个主席树。对于每一个
U
U
U操作,把第
c
n
t
−
x
−
1
cnt-x-1
cnt−x−1个主席树抠到第
c
n
t
cnt
cnt个主席树中。对于每一个
Q
Q
Q操作,直接输出第
c
n
t
cnt
cnt个主席树的第
x
x
x位即可。
时间复杂度
O
(
T
l
o
g
n
)
O(T\ log\ n)
O(T log n),空间复杂度
O
(
n
l
o
g
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过掉…