Splay 学习笔记

Splay 模板(p3369 普通平衡树)

这是 Splay 的权值树,即按照权值排序。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
 
const int N = 1000000, INF = 0x3f3f3f3f;
int n, opt, x, root = 0, cnt = 0, par[N], ch[N][2], val[N], rep[N], siz[N]; 
 
struct Splay
{
    void push_up(int x)
    {
        siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + rep[x];
    }
    int check(int x) { return ch[par[x]][1] == x ? 1 : 0; }
    void rotate(int x)
    {
        int y = par[x], z = par[y], k = check(x), w = ch[x][k ^ 1];
        ch[y][k] = w, par[w] = y;
        ch[z][check(y)] = x, par[x] = z;
        ch[x][k ^ 1] = y, par[y] = x;
        push_up(y), push_up(x);
    }
    void splay(int x, int goal)
    {
        while(par[x] != goal)
        {
           int y = par[x], z = par[y];
           if(z != goal)
           {
              if(check(x) == check(y)) rotate(y);
              else rotate(x);
           }
           rotate(x);
        }
        if(goal == 0) root = x;
    }
    void find(int x)
    {
        int cur = root;
        while(ch[cur][x > val[cur]] && x != val[cur])
           cur = ch[cur][x > val[cur]];
        splay(cur, 0);
    }
    int kth(int k)
    {
        int cur = root;
        while(true)
        {
           if(ch[cur][0] && k <= siz[ch[cur][0]]) cur = ch[cur][0];
           else if(siz[ch[cur][0]] + rep[cur] < k)
              k -= siz[ch[cur][0]] + rep[cur], cur = ch[cur][1];
           else { splay(cur, 0); return cur; }
        }
    }
    int precur(int x)
    {
        find(x);
        int cur = ch[root][0];
        if(val[root] < x) return root;
        while(ch[cur][1]) cur = ch[cur][1];
        splay(cur, 0);
        return cur;
    }
    int succes(int x)
    {
        find(x);
        int cur = ch[root][1];
        if(val[root] > x) return root;
        while(ch[cur][0]) cur = ch[cur][0];
        splay(cur, 0);
        return cur;
    } 
    void insert(int x)
    {
        int cur = root, p = 0;
        while(cur && val[cur] != x)
           p = cur, cur = ch[cur][x > val[cur]];
        if(cur) rep[cur]++;
        else
        {
           cur = ++cnt;
           if(p) ch[p][x > val[p]] = cur;
           ch[cur][0] = ch[cur][1] = 0, par[cur] = p;
           val[cur] = x, siz[cur] = rep[cur] = 1;
        }
        splay(cur, 0);
    }
    void remove(int x)
    {
        int last = precur(x), next = succes(x);
        splay(last, 0), splay(next, last);
        int del = ch[next][0];
        if(rep[del] > 1) rep[del]--, splay(del, 0);
        else ch[next][0] = 0;
        push_up(next), push_up(root);
    }
} splay;
 
int main()
{
    scanf("%d", &n);
    splay.insert(INF), splay.insert(-INF);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d%d", &opt, &x);
        if(opt == 1) splay.insert(x);
        if(opt == 2) splay.remove(x);
        if(opt == 3) splay.find(x), printf("%d
", siz[ch[root][0]]);
        if(opt == 4) printf("%d
", val[splay.kth(x + 1)]);
        if(opt == 5) printf("%d
", val[splay.precur(x)]);
        if(opt == 6) printf("%d
", val[splay.succes(x)]);
    }
    return 0;
}

Splay 模板 2(p3391 文艺平衡树)

翻转 ([l,r]) 时将 (l-1) (rotate) 到根,将 (r+1) (rotate)(l-1) 的右儿子。很显然 (>l-1)(<r+1) 的只有 (l--r)。所以 ([l,r]) 区间都在 (r+1) 的左儿子上了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
 
const int N = 1000000, INF = 0x3f3f3f3f;
int n, m, x, y, cnt = 0, root = 1;
int tag[N], num[N], par[N], siz[N], val[N], ch[N][2];
struct Splay
{
    int check(int x) { return ch[par[x]][1] == x; }
    void push_up(int x) { siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + 1; }
    int build(int l, int r, int p)
    {
        if(l > r) return 0;
        int now = ++cnt, mid = (l + r) / 2;
        if(l == r)
        siz[now] = 1;
        val[now] = num[mid], par[now] = p;
        ch[now][0] = ch[now][1] = 0;
        ch[now][0] = build(l, mid - 1, now);
        ch[now][1] = build(mid + 1, r, now);
        push_up(now);
        return now;
    }
    void push_down(int x)
    {
        if(x && !tag[x]) return ;
        tag[ch[x][0]] ^= 1, tag[ch[x][1]] ^= 1;
        swap(ch[x][0], ch[x][1]);
        tag[x] = 0;
    }
    void rotate(int x)
    {
        int y = par[x], z = par[y], k = check(x), w = ch[x][k ^ 1];
        ch[y][k] = w, par[w] = y;
        ch[z][check(y)] = x, par[x] = z;
        ch[x][k ^ 1] = y, par[y] = x;
        push_up(y), push_up(x);
    }
    void splay(int x, int goal)
    {
        while(par[x] != goal)
        {
           int y = par[x], z = par[y];
           if(z != goal)
           {
              if(check(x) == check(y)) rotate(y);
              else rotate(x);
           }
           rotate(x);
        }
        if(goal == 0) root = x;
    }
    int kth(int k)
    {
        int cur = root;
        while(true)
        {
           push_down(cur);
           if(ch[cur][0] && k <= siz[ch[cur][0]]) cur = ch[cur][0];
           else if(k > siz[ch[cur][0]] + 1)
              k -= siz[ch[cur][0]] + 1, cur = ch[cur][1];
           else { splay(cur, 0); return cur; }
        }
    }
    void reverse(int l, int r)
    {
        l = kth(l), r = kth(r + 2);
        splay(l, 0), splay(r, l);
        tag[ch[ch[root][1]][0]] ^= 1;
    }
    void print(int x)
    {
        push_down(x);
        if(ch[x][0]) print(ch[x][0]);
        if(val[x] >= 1 && val[x] <= n) printf("%d ", val[x]);
        if(ch[x][1]) print(ch[x][1]);
    }
} splay;
 
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n + 2; i++) num[i] = i - 1;
    splay.build(1, n + 2, 0);
    for(int i = 1; i <= m; i++)
    {
        scanf("%d%d", &x, &y);
        splay.reverse(x, y);
    }
    splay.print(root);
    return 0;
} 

p2234 HNOI2002 营业额统计

要求在每个数插入之前查找它的前驱和后继,很容易想到用平衡树维护。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
using namespace std;

const int N = 1000000, INF = 0x3f3f3f3f;
int n, a, now, last, next, ans = 0, root = 0, tot = 0;
int par[N], ch[N][2], rep[N], val[N], siz[N];
map <int, int> cnt;
struct Splay
{
    void push_up(int x)
    {
        siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + rep[x];
    }
    int check(int x) { return ch[par[x]][1] == x; }
    void rotate(int x)
    {
        int y = par[x], z = par[y], k = check(x), w = ch[x][k ^ 1];
        ch[y][k] = w, par[w] = y;
        ch[z][check(y)] = x, par[x] = z;
        ch[x][k ^ 1] = y, par[y] = x;
        push_up(y), push_up(x);
    }
    void splay(int x, int goal)
    {
        while(par[x] != goal)
        {
            int y = par[x], z = par[y];
            if(z != goal)
            {
                if(check(x) == check(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        if(goal == 0) root = x;
    }
    void insert(int x)
    {
        int cur = root, p = 0;
        while(cur && val[cur] != x)
            p = cur, cur = ch[cur][x > val[cur]];
        if(cur) rep[cur]++;
        else
        {
            cur = ++tot;
            if(p) ch[p][x > val[p]] = cur;
            par[cur] = p, ch[cur][0] = ch[cur][1] = 0;
            siz[cur] = rep[cur] = 1, val[cur] = x;
        }
        splay(cur, 0);
    }
    void find(int x)
    {
        int cur = root;
        while(ch[cur][x > val[cur]] && val[cur] != x)
            cur = ch[cur][x > val[cur]];
        splay(cur, 0);
    }
    int precur(int x)
    {
        find(x);
        int cur = ch[root][0];
        if(val[root] < x) return root;
        while(ch[cur][1]) cur = ch[cur][1];
        splay(cur, 0);
        return cur;
    }
    int succes(int x)
    {
        find(x);
        int cur = ch[root][1];
        if(val[root] > x) return root;
        while(ch[cur][0]) cur = ch[cur][0];
        splay(cur, 0);
        return cur;
    }
} splay;

int main()
{
    scanf("%d%d", &n, &a);
    splay.insert(INF), splay.insert(-INF);
    splay.insert(a), ans = a, cnt[a]++;
    for(int i = 2; i <= n; i++)
    {
        scanf("%d", &a);
        if(cnt[a] == 0)
        {
            now = INF;
            last = splay.precur(a), next = splay.succes(a);
            if(val[last] != -INF) now = a - val[last];
            if(val[next] != INF) now = min(now, val[next] - a);
            if(now != INF) ans += now;
        }
        splay.insert(a), cnt[a]++;
    }
    printf("%d", ans);
    return 0;
} 

SP1716 GSS3 - Can you answer these queries III

这题跟 Splay 没有什么关系,但它是下一题的前置芝士。

因为要求支持带修的最大子段和,可以使用线段树维护。主要问题是如何使用两个儿子的值求出父亲的值(即 push_up 操作的实现)

(prel) 表示从区间最左端开始的最大子段和,(prer) 表示从区间最右端点开始的最大子段和,(sum) 表示区间和,(res) 表示区间的最大子段和。(用 (l) 表示左儿子,(r) 表示右儿子)

(sum) 的更新比较简单,可以直接更新。

(prel) 的更新有两种情况:仅存在于左子树中;跨过左子树进入右子树中。在两者之间取 (max)(prer) 的更新同理。

(prel[x] = max(prel[l], sum[l] + prel[r]))

(res) 的更新有三种情况:左子树的 (res),右子树的 (res),左子树的 (prer) + 右子树的 (prel)。在三者之间取 (max)

(res[x] = max(max(res[l], res[r]), prer[l] + prel[r]))

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 1000000;
int n, q, opt, x, y, a[N];
struct node
{
    int sum, prel, prer, res;
} st[N];
struct Segment_Tree
{
    void push_up(int x)
    {
        st[x].sum = st[x * 2].sum + st[x * 2 + 1].sum;
        st[x].prel = max(st[x * 2].prel, st[x * 2].sum + st[x * 2 + 1].prel);
        st[x].prer = max(st[x * 2 + 1].prer, st[x * 2 + 1].sum + st[x * 2].prer);
        st[x].res = max(st[x * 2].res, st[x * 2 + 1].res);
        st[x].res = max(st[x].res, st[x * 2].prer + st[x * 2 + 1].prel);
    }
    void build(int x, int l, int r)
    {
        if(l == r)
        {
            st[x].sum = a[l];
            st[x].prel = st[x].prer = st[x].res = a[l];
            return ;
        }
        int mid = (l + r) / 2;
        build(x * 2, l, mid);
        build(x * 2 + 1, mid + 1, r);
        push_up(x);
    }
    void update(int x, int l, int r, int std, int k)
    {
        if(l > std || r < std) return ;
        if(std == l && std == r)
        {
            st[x].sum = k;
            st[x].prel = st[x].prer = st[x].res = k;
            return ;
        }
        int mid = (l + r) / 2;
        update(x * 2, l, mid, std, k);
        update(x * 2 + 1, mid + 1, r, std, k);
        push_up(x);
    }
    node query(int x, int l, int r, int stdl, int stdr)
    {
        if(stdl <= l && stdr >= r) return st[x];
        int mid = (l + r) / 2;
        if(stdr <= mid) return query(x * 2, l, mid, stdl, stdr);
        if(stdl > mid) return query(x * 2 + 1, mid + 1, r, stdl, stdr);
        node A = query(x * 2, l, mid, stdl, stdr), res;
        node B = query(x * 2 + 1, mid + 1, r, stdl, stdr);
        res.prel = max(A.prel, A.sum + B.prel);
        res.prer = max(B.prer, B.sum + A.prer);
        res.res = max(A.prer + B.prel, max(A.res, B.res));
        return res;
    }
} tree;

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    tree.build(1, 1, n);
    scanf("%d", &q);
    for(int i = 1; i <= q; i++)
    {
        scanf("%d%d%d", &opt, &x, &y);
        if(opt == 0) tree.update(1, 1, n, x, y);
        else printf("%d
", tree.query(1, 1, n, x, y).res);
    }
    return 0;
}

p2042 NOI2005 维护数列

恶心题,不过可以提高码力。对于 Insert 操作,先对所有需要 Insert 的数建立一棵平衡树,插入到 (>posi)(<posi+1) 的位置。

另外,本题的最大子段和与上一题大致相同,在旋转的时候,(prel[x])(prer[x]) 需要互换。另外需要注意一些细节,例如最大子段和至少要选择一个数字。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

const int N = 10000000, INF = 0x3f3f3f3f;
struct node { int prel, prer, sum, res; } st[N];
int n, m, a, posi, tot, c, root = 1, cnt = 0;
int par[N], val[N], num[N], ch[N][2], siz[N], flt[N], rev[N];
string opt; 
queue <int> q;
struct Splay
{
    int check(int x) { return ch[par[x]][1] == x; }
    void push_up(int x)
    {
        int l = ch[x][0], r = ch[x][1];
        siz[x] = siz[l] + siz[r] + 1;
        st[x].sum = st[l].sum + st[r].sum + val[x];
        if(!l) st[l].res = -INF;
        if(!r) st[r].res = -INF;
        st[x].prel = max(st[l].prel, st[l].sum + st[r].prel + val[x]);
        st[x].prer = max(st[r].prer, st[r].sum + st[l].prer + val[x]);
        st[x].res = max(max(st[l].res, st[r].res), st[l].prer + st[r].prel + val[x]);
    }
    void push_down(int x)
    {
        int l = ch[x][0], r = ch[x][1];
        if(flt[x])
        {
            if(l)
            {
                val[l] = val[x], flt[l] = 1, st[l].sum = val[x] * siz[l];
                if(val[x] >= 0)
                    st[l].res = st[l].prel = st[l].prer = st[l].sum;
                else st[l].res = val[x], st[l].prel = st[l].prer = 0;
            }
            if(r)
            {
                val[r] = val[x], flt[r] = 1, st[r].sum = val[x] * siz[r];
                if(val[x] >= 0)
                    st[r].res = st[r].prel = st[r].prer = st[r].sum;
                else st[r].res = val[x], st[r].prel = st[r].prer = 0;
            }
            flt[x] = rev[x] = 0;
        }
        if(rev[x])
        {
            if(l) rev[l] ^= 1;
            if(r) rev[r] ^= 1;
            swap(ch[x][0], ch[x][1]);
            if(l) swap(st[l].prel, st[l].prer);
            if(r) swap(st[r].prel, st[r].prer);
            rev[x] = 0;
        }
        push_up(x);
    }
    int newID()
    {
        if(!q.empty())
        {
            int res = q.front();
            q.pop(); return res;
        }
        else return ++cnt;
    }
    int build(int l, int r, int p)
    {
        if(l > r) return 0;
        int now = newID(), mid = (l + r) / 2;
        rev[now] = flt[now] = 0;
        val[now] = st[now].sum = num[mid];
        siz[now] = 1, par[now] = p;
        st[now].prel = st[now].prer = st[now].res = num[mid];
        ch[now][0] = build(l, mid - 1, now);
        ch[now][1] = build(mid + 1, r, now);
        push_up(now);
        return now;
    }
    void rotate(int x)
    {
        int y = par[x], z = par[y], k = check(x), w = ch[x][k ^ 1];
        ch[y][k] = w, par[w] = y;
        ch[z][check(y)] = x, par[x] = z;
        ch[x][k ^ 1] = y, par[y] = x;
        push_up(y), push_up(x);
    }
    void splay(int x, int goal)
    {
        while(par[x] != goal)
        {
            int y = par[x], z = par[y];
            if(z != goal)
            {
                if(check(x) == check(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        if(goal == 0) root = x;
    }
    int kth(int k)
    {
        int cur = root;
        while(true)
        {
            push_down(cur);
            if(ch[cur][0] && siz[ch[cur][0]] >= k) cur = ch[cur][0];
            else if(siz[ch[cur][0]] + 1 < k)
                k -= siz[ch[cur][0]] + 1, cur = ch[cur][1];
            else { splay(cur, 0); return cur; }
        }
    }
    void rubish(int x)
    {
        q.push(x);
        if(ch[x][0]) rubish(ch[x][0]);
        if(ch[x][1]) rubish(ch[x][1]);
    }
    void insert(int start, int rt)
    {
        int l = kth(start + 1), r = kth(start + 2);
        splay(l, 0), splay(r, l);
        ch[ch[root][1]][0] = rt;
        par[rt] = ch[root][1];
        push_up(ch[root][1]), push_up(root);
    }
    void remove(int l, int r)
    {
        l = kth(l), r = kth(r + 2);
        splay(l, 0), splay(r, l);
        rubish(ch[ch[root][1]][0]);
        ch[ch[root][1]][0] = 0;
        push_up(ch[root][1]), push_up(root);
    }
    void flat(int l, int r, int k)
    {
        l = kth(l), r = kth(r + 2), splay(l, 0), splay(r, l);
        int x = ch[ch[root][1]][0];
        flt[x] = 1, val[x] = k, st[x].sum = k * siz[x];
        if(k >= 0) st[x].prel = st[x].prer = st[x].res = st[x].sum;
        else st[x].prel = st[x].prer = 0, st[x].res = k;
        push_up(ch[root][1]), push_up(root);
    }
    void reverse(int l, int r)
    {
        l = kth(l), r = kth(r + 2), splay(l, 0), splay(r, l);
        int x = ch[ch[root][1]][0];
        rev[x] ^= 1, swap(st[x].prel, st[x].prer);
        push_down(x);
        push_up(ch[root][1]), push_up(root);
    }
    int getsum(int l, int r)
    {
        l = kth(l), r = kth(r + 2);
        splay(l, 0), splay(r, l);
        push_up(ch[root][1]), push_up(root);
        return st[ch[ch[root][1]][0]].sum;
    }
} splay;

int main()
{
    scanf("%d%d", &n, &m);
    num[1] = num[n + 2] = -INF;
    for(int i = 2; i <= n + 1; i++) scanf("%d", &num[i]);
    root = splay.build(1, n + 2, 0);
    for(int i = 1; i <= m; i++)
    {
        cin >> opt;
        if(opt[0] == 'I')
        {
            scanf("%d%d", &posi, &tot);
            for(int j = 1; j <= tot; j++) scanf("%d", &num[j]);
            splay.insert(posi, splay.build(1, tot, splay.newID()));
        }
        if(opt[0] == 'D')
        {
            scanf("%d%d", &posi, &tot);
            splay.remove(posi, posi + tot - 1);
        }
        if(opt[0] == 'M' && opt[opt.size() - 1] == 'E')
        {
            scanf("%d%d%d", &posi, &tot, &c);
            splay.flat(posi, posi + tot - 1, c);
        }
        if(opt[0] == 'R')
        {
            scanf("%d%d", &posi, &tot);
            splay.reverse(posi, posi + tot - 1);
        }
        if(opt[0] == 'G')
        {
            scanf("%d%d", &posi, &tot);
            printf("%d
", splay.getsum(posi, posi + tot - 1));
        }
        if(opt[0] == 'M' && opt[opt.size() - 1] == 'M')
            printf("%d
", st[root].res);
    }
    return 0;
}

p3224 HNOI2012 永无乡

初始对每一个连通块都建立一棵平衡树。每次加进一座桥,就把相应的两棵平衡树合并。使用启发式合并,每次把节点数小的树合并到节点数大的树上。

因为并不会卡常,吸氧才通过最后一个点。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1000000;
int n, m, cnt = 0, root = 0, a, b, q;
int fa[N], val[N], siz[N], num[N], rem[N], ch[N][2], par[N], rot[N];
char opt;

inline int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

struct Splay
{
    inline int check(int x) { return ch[par[x]][1] == x; }
    inline void push_up(int x) { siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + 1; }
    inline void rotate(int x)
    {
        int y = par[x], z = par[y], k = check(x), w = ch[x][k ^ 1];
        ch[y][k] = w, par[w] = y;
        ch[z][check(y)] = x, par[x] = z;
        ch[x][k ^ 1] = y, par[y] = x;
        push_up(y), push_up(x);
    }
    inline void splay(int x, int goal, int fd)
    {
        while(par[x] != goal)
        {
            int y = par[x], z = par[y];
            if(z != goal)
            {
                if(check(x) == check(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        if(goal == 0) rot[fd] = x;
    }
    inline void insert(int x, int fd)
    {
        int cur = rot[fd], p = 0;
        while(cur) p = cur, cur = ch[cur][val[x] > val[cur]];
        par[x] = p, ch[p][val[x] > val[p]] = x;
        splay(x, 0, fd);
    }
    void remem(int x)
    {
        rem[++cnt] = x;
        if(ch[x][0]) remem(ch[x][0]);
        if(ch[x][1]) remem(ch[x][1]);
        ch[x][0] = ch[x][1] = 0, siz[x] = 1;
    }
    inline void add(int x, int fdy)
    {
        remem(x);
        for(int i = 1; i <= cnt; i++) insert(rem[i], fdy);
    }
    inline int kth(int k, int fd)
    {
        int cur = rot[fd];
        while(true)
        {
            if(ch[cur][0] && siz[ch[cur][0]] >= k) cur = ch[cur][0];
            else if(siz[ch[cur][0]] + 1 < k)
                k -= siz[ch[cur][0]] + 1, cur = ch[cur][1];
            else { splay(cur, 0, fd); return cur; }
        }
    }
} splay; 

inline int read()
{
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
    while(c >= '0' && c <= '9') { x = x * 10 + c - 48; c = getchar(); } 
    return f * x;
}

int main()
{
    n = read(), m = read();
    for(int i = 1; i <= n; i++) val[i] = read();
    for(int i = 1; i <= n; i++) fa[i] = rot[i] = i, siz[i] = 1;
    for(int i = 1; i <= m; i++)
    {
        a = read(), b = read();
        splay.insert(rot[find(a)], find(b));
        fa[find(a)] = find(b);
    }
    q = read();
    for(int i = 1; i <= q; i++)
    {
        cin >> opt;
        if(opt == 'B')
        {
            a = read(), b = read();
            cnt = 0;
            if(find(a) == find(b)) continue;
            if(siz[rot[find(a)]] > siz[rot[find(b)]]) swap(a, b);
            splay.add(rot[find(a)], find(b));
            fa[find(a)] = find(b);
        }
        if(opt == 'Q')
        {
            a = read(), b = read();
            if(siz[rot[find(a)]] < b) printf("-1
");
            else printf("%d
", splay.kth(b, find(a)));
        }
    }
    return 0;
}

p1486 NOI2004 郁闷的出纳员

首先注意到加工资和扣工资的操作最多只有 (100) 个,可以直接暴力,并且把工资不足的人暴力删掉。

另外,不要在 (dfs) 整棵树的时候做任何操作,否则会因为改变父子关系导致 MLE

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 101000;
int n, min_, k, leave = 0, root = 0, cnt = 0, tot;
int ch[N][2], par[N], siz[N], rep[N], val[N], del[N];
char opt;
struct Splay
{
    int check(int x) { return ch[par[x]][1] == x; }
    void push_up(int x)
    {
        siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + rep[x];
    }
    void rotate(int x)
    {
        int y = par[x], z = par[y], k = check(x), w = ch[x][k ^ 1];
        ch[y][k] = w, par[w] = y;
        ch[z][check(y)] = x, par[x] = z;
        ch[x][k ^ 1] = y, par[y] = x;
        push_up(y), push_up(x); 
    }
    void splay(int x, int goal)
    {
        while(par[x] != goal)
        {
            int y = par[x], z = par[y];
            if(z != goal)
            {
                if(check(x) == check(y)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        if(goal == 0) root = x;
    }
    void insert(int x)
    {
        int cur = root, p = 0;
        while(cur && val[cur] != x)
            p = cur, cur = ch[cur][x > val[cur]];
        if(cur) rep[cur]++;
        else
        {
            cur = ++cnt;
            if(p) ch[p][x > val[p]] = cur;
            par[cur] = p, ch[cur][0] = ch[cur][1] = 0;
            val[cur] = x, siz[cur] = rep[cur] = 1;
        }
        splay(cur, 0);
    }
    int kth(int k)
    {
        int cur = root;
        while(true)
        {
            if(ch[cur][0] && siz[ch[cur][0]] >= k) cur = ch[cur][0];
            else if(siz[ch[cur][0]] + rep[cur] < k)
                k -= siz[ch[cur][0]] + rep[cur], cur = ch[cur][1];
            else { splay(cur, 0); return cur; }
        }
    }
    int precur(int x)
    {
        splay(x, 0);
        int cur = ch[root][0];
        while(ch[cur][1]) cur = ch[cur][1];
        return cur;
    }
    int succes(int x)
    {
        splay(x, 0);
        int cur = ch[root][1];
        while(ch[cur][0]) cur = ch[cur][0];
        return cur;
    }
    void remove(int x)
    {
        int last = precur(x), next = succes(x);
        splay(last, 0), splay(next, last);
        leave += rep[ch[next][0]], ch[next][0] = 0;
        push_up(next), push_up(last);
    }
    void dfs(int x, int k)
    {
        if(x > 2) val[x] += k;
        if(val[x] < min_ && x > 2) del[++tot] = x;
        if(ch[x][0]) dfs(ch[x][0], k);
        if(ch[x][1]) dfs(ch[x][1], k);
    }
    void work(int k)
    {
        tot = 0; dfs(root, k);
        for(int i = 1; i <= tot; i++) remove(del[i]);
    }
} splay;

int main()
{
    scanf("%d%d", &n, &min_);
    splay.insert(0x3f3f3f3f), splay.insert(-0x3f3f3f3f);
    for(int i = 1; i <= n; i++)
    {
        cin >> opt >> k;
        tot = 0;
        if(opt == 'I' && k >= min_) splay.insert(k);
        if(opt == 'A') splay.work(k);
        if(opt == 'S') splay.work(-k);
        if(opt == 'F')
        {
            int b = siz[root] - k;
            if(k > siz[root] - 2) printf("-1
");
            else printf("%d
", val[splay.kth(b)]);
        }
    }
    printf("%d", leave);
    return 0;
}
原文地址:https://www.cnblogs.com/Andy-park/p/12388293.html