P1383 高级打字机 *

Link
主席树

思路

用 size 数组表示当前子树节点个数,新加入节点时,若 s i z e ( l c ) = = m i d − l + 1 size(lc) == mid - l + 1 size(lc)==midl+1,则递归右子树,否则递归左子树。
相对应的,询问第 i 个位置的字母时,若 i ≤ s i z e ( l c ( p ) ) i \leq size(lc(p)) isize(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;
}





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值