[SCOI2014]方伯伯的OJ 题解

传送门

我佛了,我这么大常数的写法竟然跑得还不算特别慢。。。

首先想到用一个splay按排名维护队列,那么操作都挺sb的,随便写写就行了吧。

然后就发现有问题, n ≤ 1 0 8 n\leq10^8 n108,这意味着好像不能把整棵树建出来(可以吗?我也不清楚==)。于是我们考虑一种结点分裂的做法,就是把一段区间合成一个结点,用到的时候再分裂。

那么既然这样我们还需要一个map来知道编号x对应的结点是哪一个。

然后问题出现了,要分裂结点的话意味着在map上要修改,而且不是改单点而是区间修改,于是我就把map改成了线段树

反正写得挺麻烦的不过仔细实现一下也还行。。。

另外为了不让树被卡成链,我在结点分裂的时候搞了一个可能没什么用的判断,就是可能分裂出一个左孩子也可能分裂出一个右孩子。脑补一下觉得有点用吧?…

#include <cctype>
#include <cstdio>
#include <climits>
#include <algorithm>

template <typename T> inline void read(T& x) {
    int f = 0, c = getchar(); x = 0;
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
    if (f) x = -x;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
    read(x); read(args...); 
}
template <typename T> void write(T x) {
    if (x < 0) x = -x, putchar('-');
    if (x > 9) write(x / 10);
    putchar(x % 10 + 48);
}
template <typename T> inline void writeln(T x) { write(x); puts(""); }
template <typename T> inline bool chkmin(T& x, const T& y) { return y < x ? (x = y, true) : false; }
template <typename T> inline bool chkmax(T& x, const T& y) { return x < y ? (x = y, true) : false; }

const int maxn = 1e5 + 7;

int n, m;

struct Sgt {
    struct Node {
        int val, lc, rc;
        int tag;
        Node() : val(0), lc(0), rc(0), tag(0) {}
    };
    Node T[maxn << 6];
    int tot, root;
    void update(int o) {
        if (T[T[o].lc].val == T[T[o].rc].val) T[o].val = T[T[o].lc].val;
        else T[o].val = 0;
    }
    void pushdown(int o) {
        if (T[o].tag) {
            if (!T[o].lc) T[o].lc = ++tot;
            if (!T[o].rc) T[o].rc = ++tot;
            T[T[o].lc].tag = T[T[o].rc].tag = T[o].tag;
            T[T[o].lc].val = T[T[o].rc].val = T[o].tag;
            T[o].tag = 0;
        }
    }
    void modify(int &o, int lb, int rb, int l, int r, int na) {
        if (l > rb || r < lb) return;
        if (!o) o = ++tot;
        if (l <= lb && r >= rb) {
            T[o].tag = T[o].val = na;
            return;
        }
        int mid = (lb + rb) >> 1;
        pushdown(o);
        modify(T[o].lc, lb, mid, l, r, na);
        modify(T[o].rc, mid + 1, rb, l, r, na);
        update(o);
    }
    int query(int o, int lb, int rb, int p) {
        if (T[o].val) return T[o].val;
        int mid = (lb + rb) >> 1;
        pushdown(o);
        if (p <= mid) return query(T[o].lc, lb, mid, p);
        else return query(T[o].rc, mid + 1, rb, p);
    }
    void modify(int l, int r, int na) { modify(root, 1, 2e8, l, r, na); }
    int query(int x) { return query(root, 1, 2e8, x); }
};
Sgt pos;

int fa[maxn << 6], ch[maxn << 6][2], lb[maxn << 6], rb[maxn << 6], size[maxn << 6], tot, root;

inline int iden(int x) { return ch[fa[x]][0] == x ? 0 : ch[fa[x]][1] == x ? 1 : -1; }
inline void update(int x) { size[x] = rb[x] - lb[x] + 1 + size[ch[x][0]] + size[ch[x][1]]; }
inline int split(int x) {
    static int state = 1;
    if (lb[x] < rb[x]) {
        state ^= 1;
        int mid = (lb[x] + rb[x]) >> 1;
        int y = ++tot, z = ch[x][state];
        if (z) fa[z] = y;
        ch[y][state] = z;
        fa[ch[x][state] = y] = x;
        if (state) {
            lb[y] = mid + 1; rb[y] = rb[x];
            rb[x] = mid;
        } else {
            lb[y] = lb[x]; rb[y] = mid;
            lb[x] = mid + 1;
        }
        pos.modify(lb[y], rb[y], y);
        update(y);
        return state;
    } else return -1;
}
inline void rotate(int x) {
    int d = iden(x), y = fa[x];
    if (~iden(y)) ch[fa[y]][iden(y)] = x;
    fa[x] = fa[y];
    if ((ch[y][d] = ch[x][d ^ 1])) fa[ch[x][d ^ 1]] = y;
    fa[ch[x][d ^ 1] = y] = x;
    update(y); update(x);
}
inline void splay(int x, int &k) {
    if (x == k) return;
    int p = fa[k];
    while (fa[x] != p) {
        int y = fa[x];
        if (fa[y] != p) rotate(iden(y) ^ iden(x) ? x : y);
        rotate(x);
    }
    k = x;
}
inline int get_single(int x, int id) {
    while (lb[x] < rb[x]) {
        int d = split(x);
        if (lb[x] > id || id > rb[x]) x = ch[x][d];
    }
    return x;
}
// d == 1 move_to_top;  d == 0 move_to_bottom
inline int move(int x, int id, int d) {
    x = get_single(x, id);
    splay(x, root);
    int ans = size[ch[x][0]] + 1;
    int y = ch[x][d];
    if (y) {
        while (ch[y][d ^ 1]) y = ch[y][d ^ 1];
        splay(y, ch[x][d]);
        fa[ch[ch[x][d]][d ^ 1] = ch[x][d ^ 1]] = ch[x][d];
        ch[x][d ^ 1] = 0;
        update(ch[x][d]);
    } else {
        std::swap(ch[x][0], ch[x][1]);
    }
    return ans;
}
inline int findkth(int k) {
    int x = root;
    while (true) {
        if (ch[x][0] && size[ch[x][0]] >= k) x = ch[x][0];
        else {
            if (ch[x][0]) k -= size[ch[x][0]];
            if (k <= rb[x] - lb[x] + 1) {
                splay(x, root);
                return lb[x] + k - 1;
            }
            k -= rb[x] - lb[x] + 1;
            x = ch[x][1];
        }
    }
}
inline int modify(int idx, int idy) {
    int x = pos.query(idx);
    x = get_single(x, idx);
    splay(x, root);
    int ans = size[ch[x][0]] + 1;
    pos.modify(idx, idx, 0);
    lb[x] = rb[x] = idy;
    pos.modify(idy, idy, x);
    return ans;
}

int main() {
    read(n, m);
    pos.modify(1, n, root = ++tot);
    lb[root] = 1; rb[root] = size[root] = n;
    int lastans = 0;
    while (m--) {
        int q; read(q);
        if (q == 1) {
            int x, y; read(x, y);
            x -= lastans; y -= lastans;
            writeln(lastans = modify(x, y));
        } else if (q == 2) {
            int x; read(x); x -= lastans;
            writeln(lastans = move(pos.query(x), x, 1));
        } else if (q == 3) {
            int x; read(x); x -= lastans;
            writeln(lastans = move(pos.query(x), x, 0));
        } else {
            int k; read(k); k -= lastans;
            writeln(lastans = findkth(k));
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值