[CodeVS 1343] 蚱蜢:Splay Tree

题意:N个数,J个操作(2 ≤ N ≤ 100 000, 1 ≤ J ≤ 100 000)。每个操作将第a个数往前或后移动b个,并询问跨越的这些数中的最大值,保证操作有效。

要求支持区间查询、增添删除,Splay是个不错的选择。

把第a个数删掉,再插到第b个数前面就好啦。有没有更有针对性的做法呢?

我是这样做的:假设把x移到y前面,x、y把一列数分为三段:a x b y c -> a b x y c

把x删除,插到y前面等价于把b和a合并。正好,为了查区间最大值,我们已经把b提取出来了。

#include <cstdio>
#include <algorithm>
const int MAX_N = 1e5, inf = 1<<30;
int N, a[MAX_N];

struct Splay {
    static const int n = MAX_N+4, nil = n-1;
    int ptr, &root, ch[n][2], fa[n], sz[n], v[n], mx[n];
    int type(int x) { return x == ch[fa[x]][1]; }
    void up(int x) { sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1; mx[x] = std::max(v[x], std::max(mx[ch[x][0]], mx[ch[x][1]])); }
    void set(int x, int d, int y) { fa[ch[x][d] = y] = x; }
    void rot(int x, int d)
    {
        int y = ch[x][d];
        set(fa[x], type(x), y);
        set(x, d, ch[y][d^1]);
        set(y, d^1, x);
        up(x);
        up(y);
    }
    void splay(int x, int p=0)
    {
        int y;
        while ((y = fa[x]) != p) {
            int z = fa[y], t1 = type(x);
            if (z != p) {
                int t2 = type(y);
                if (t1 == t2)
                    rot(z, t2), rot(y, t1);
                else
                    rot(y, t1), rot(z, t2);
            } else
                rot(y, t1);
        }
    }
    int new_node(int c)
    {
        ch[ptr][0] = ch[ptr][1] = nil;
        sz[ptr] = 1;
        v[ptr] = mx[ptr] = c;
        return ptr++;
    }
    Splay(): ptr(1), root(ch[0][0])
    {
        sz[nil] = 0;
        v[nil] = mx[nil] = -inf;
        set(0, 0, new_node(0));
        set(1, 1, new_node(0));
    }
    void build()
    {
        build(0, N-1, 2, 0);
        up(2);
        up(1);
    }
    void build(int l, int r, int p, int t)
    {
        if (l > r) return;
        int m = (l+r)/2, x = new_node(a[m]);
        set(p, t, x);
        build(l, m-1, x, 0);
        build(m+1, r, x, 1);
        up(x);
    }
    void join(int x, int y, int t)
    {
        int p = x;
        while (ch[p][t] != nil)
            p = ch[p][t];
        splay(p, fa[x]);
        set(p, t, y);
        up(p);
    }
    int kth(int k, int p=0)
    {
        int x = root;
        while (x != nil) {
            int s = sz[ch[x][0]];
            if (k == s+1) return splay(x, p), x;
            if (s >= k) x = ch[x][0];
            else k -= s+1, x = ch[x][1];
        }
    }
    int jump(int a, int b)
    {
        ++a, ++b;
        int t = a>b, x = kth(a), y = kth(b, x), z = ch[y][t], r = mx[z];
        ch[y][t] = nil;
        up(y);
        join(ch[x][t], z, t^1);
        return r;
    }
} T;

int main()
{
    int J;
    scanf("%d %d", &N, &J);
    for (int i = 0; i < N; ++i)
        scanf("%d", &a[i]);
    T.build();
    while (J--) {
        int a, b;
        char o;
        scanf("%d %c %d", &a, &o, &b);
        printf("%d\n", T.jump(a, a+(o == 'D' ? b+1 : -b-1)));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值