Link
主席树
思路
用 size 数组表示当前子树节点个数,新加入节点时,若
s
i
z
e
(
l
c
)
=
=
m
i
d
−
l
+
1
size(lc) == mid - l + 1
size(lc)==mid−l+1,则递归右子树,否则递归左子树。
相对应的,询问第 i 个位置的字母时,若
i
≤
s
i
z
e
(
l
c
(
p
)
)
i \leq size(lc(p))
i≤size(lc(p)),则递归左子树,否则递归右子树,并将 i 减去左子树的大小。
注意撤销操作时,最好直接新建根节点,一开始想当然直接将rt减去撤销的步数,然而如果不清空那几块的话显然会有问题,所以还是直接新建更好,时间空间复杂度都低。
代码
// Decline is inevitable,
// Romance will last forever.
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const int maxm = 1e3 + 10;
struct SegmentTree {
int lc, rc;
int size;
char sum;
#define lc(p) tree[p].lc
#define rc(p) tree[p].rc
#define sum(p) tree[p].sum
#define size(p) tree[p].size
} tree[maxn << 5];
int rt[maxn], tot;
int n, m;
int insert(int pre, int l, int r, char a) {
int p = ++tot;
lc(p) = lc(pre); rc(p) = rc(pre); size(p) = size(pre);
sum(p) = sum(pre);
if(l > r) return p;
if(l == r) {
size(p) = 1;
sum(p) = a;
return p;
}
int mid = (l + r) >> 1;
if(size(lc(p)) == mid - l + 1)
rc(p) = insert(rc(pre), mid + 1, r, a);
else
lc(p) = insert(lc(pre), l, mid, a);
size(p) = size(lc(p)) + size(rc(p));
return p;
}
char query(int p, int l, int r, int loc) {
if(l >= r) {
return sum(p);
}
int mid = (l + r) >> 1;
if(loc <= size(lc(p)))
return query(lc(p), l, mid, loc);
else
return query(rc(p), mid + 1, r, loc - size(lc(p)));
}
void solve() {
cin >> n;
tot = 0;
int now = 0;
for(int i = 1; i <= n; i++) {
char c;
cin >> c;
if(c == 'T') {
now++;
char a;
cin >> a;
// int size = size(rt[now-1]);
rt[now] = insert(rt[now-1], 1, n, a);
}
else if(c == 'U') {
int a;
cin >> a;
// now -= a;
// now = max(0, now);
++now;
rt[now] = rt[now - a - 1];
}
else {
int loc;
cin >> loc;
char q = query(rt[now], 1, n, loc);
cout << q << endl;
}
}
}
signed main() {
// ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
// int T; scanf("%d", &T); while(T--)
// freopen("1channel00.txt","r",stdin);
// freopen("2.txt","w",stdout);
// int T; cin >> T; while(T--)
solve();
return 0;
}