[SCOI2014]方伯伯的OJ 题解

[SCOI2014]方伯伯的OJ 题解

题目地址:洛谷:【P3285】[SCOI2014]方伯伯的OJ – 洛谷、BZOJ:Problem 3595. — [Scoi2014]方伯伯的Oj

题目描述

方伯伯正在做他的Oj。现在他在处理Oj上的用户排名问题。Oj上注册了n个用户,编号为1~n“,一开始他们按照编号排名。
方伯伯会按照心情对这些用户做以下四种操作,修改用户的排名和编号:

1.操作格式为1 x y,意味着将编号为x的用户编号改为y,而排名不变,执行完该操作后需要输出该用户在队列中的位置,数据保证x必然出现在队列中,同时,1是一个当前不在排名中的编号。
2.操作格式为2 x,意味着将编号为x的用户的排名提升到第一位,执行完该操作后需要输出执行该操作前编号为x用户的排名。
3.操作格式为3 x,意味着将编号为x的用户的排名降到最后一位,执行完该操作后需要输出执行该操作前编号为x用户的排名。
4.操作格式为4 k,意味着查询当前排名为k的用户编号,执行完该操作后需要输出当前操作用户的编号。

但同时为了防止别人监听自己的工作,方伯伯对他的操作进行了加密,即将四种操作的格式分别改为了:

  • 1 x+a y+a
  • 2 x+a
  • 3 x+a
  • 4 k+a

其中a为上一次操作得到的输出,一开始a=0。
例如:上一次操作得到的输出是5这一次操作的输入为:1 13 15因为这个输入是经过加密后的,所以你应该处理的操作是1 8 10现在你截获了方伯伯的所有操作,希望你能给出结果。

输入输出格式

输入格式:
输入的第1行包含2个用空格分隔的整数n和m,表示初始用户数和操作数。此后有m行,每行是一个询问,询问格式如上所示。

输出格式:
输出包含m行。每行包含一个整数,其中第i行的整数表示第i个操作的输出。

输入输出样例

输入样例#1:

10 10
1 2 11
3 13
2 5
3 7
2 8
2 10
2 11
3 14
2 18
4 9

输出样例#1:

2
2
2
4
3
5
5
7
8
11

说明

对于 100% 的数据,1 <= n <= 10^8,1 <= m <= 10^5
输入保证对于所有的操作 1,2,3,x 必然已经出现在队列中,同时对于所有操作 1,1 <= y <= 2 * 10^8,并且y 没有出现在队列中。
对于所有操作 4,保证 1 <= k <= n。

题解

处理编号与splay节点编号之间的对应关系,我们可以考虑使用map。
第一位和最后一位的处理方法类似[ZJOI2006]书架 题解 | KSkun’s Blog
改编号的操作,只需要把原来的编号从map中删去再加入新的编号即可。
但是这个n的数据范围有点吓人呀,我们考虑用一个节点表示一段连续区间,等到用到这个区间中某个值的时候再把它割成[l, i-1]、[i, i]、[i+1, r]三段,这样的话每次操作前要先割区间,然后继续。map存的也不再是编号-splay节点的关系了,而是区间左端点-splay节点,这样我们每次找到小于等于要处理编号的节点即可。

代码

// Code by KSkun, 2018/4
#include <cstdio>
#include <cstring>

#include <algorithm>
#include <map>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF 
        : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 1000005, INF = 1e9;

std::map<int, int> spn;

struct Node {
    int fa, ch[2], l, r, siz;
} tr[MAXN];
int rt, tot;

inline void update(int p) {
    tr[p].siz = tr[tr[p].ch[0]].siz + tr[tr[p].ch[1]].siz + tr[p].r - tr[p].l + 1;
}

inline bool isleft(int p) {
    return tr[tr[p].fa].ch[0] == p;
}

inline void rotate(int p) {
    bool t = !isleft(p); int fa = tr[p].fa, ffa = tr[fa].fa;
    tr[p].fa = ffa; if(ffa) tr[ffa].ch[!isleft(fa)] = p;
    tr[fa].ch[t] = tr[p].ch[!t]; tr[tr[fa].ch[t]].fa = fa;
    tr[fa].fa = p; tr[p].ch[!t] = fa;
    update(fa);
    if(!tr[p].fa) rt = p;
}

inline void splay(int p, int tar) {
    for(int fa = tr[p].fa; fa != tar; rotate(p), fa = tr[p].fa) {
        if(tr[fa].fa != tar) rotate(isleft(p) == isleft(fa) ? fa : p);
    }
    update(p);
}

inline int queryn(int p, int rk) {
    if(rk <= tr[tr[p].ch[0]].siz) {
        return queryn(tr[p].ch[0], rk);
    }
    rk -= tr[tr[p].ch[0]].siz;
    if(rk <= tr[p].r - tr[p].l + 1) {
        splay(p, 0);
        return tr[p].l + rk - 1;
    }
    rk -= tr[p].r - tr[p].l + 1;
    return queryn(tr[p].ch[1], rk);
}

inline int querypre() {
    int p = tr[rt].ch[0];
    while(tr[p].ch[1]) p = tr[p].ch[1];
    return p;
}

inline int querynxt() {
    int p = tr[rt].ch[1];
    while(tr[p].ch[0]) p = tr[p].ch[0];
    return p;
}

inline void split(int p, int id) {
    if(tr[p].l < id) {
        int q = ++tot;
        tr[q].ch[0] = tr[p].ch[0];
        tr[tr[q].ch[0]].fa = q;
        tr[p].ch[0] = q;
        tr[q].l = tr[p].l; tr[q].r = id - 1;
        tr[q].fa = p;
        update(q);
        spn[tr[q].l] = q;
    }
    if(tr[p].r > id) {
        int q = ++tot;
        tr[q].ch[1] = tr[p].ch[1];
        tr[tr[q].ch[1]].fa = q;
        tr[p].ch[1] = q;
        tr[q].l = id + 1; tr[q].r = tr[p].r;
        tr[q].fa = p;
        update(q);
        spn[tr[q].l] = q;
    }
    tr[p].l = tr[p].r = id;
    update(p);
    spn[id] = p;
}

inline int top(int p) {
    splay(p, 0);
    int res = tr[tr[p].ch[0]].siz + 1;
    if(!tr[p].ch[0]) return res;
    if(!tr[p].ch[1]) {
        std::swap(tr[p].ch[0], tr[p].ch[1]);
        return res;
    }
    int q = querynxt();
    split(q, tr[q].l);
    splay(q, p);
    tr[q].ch[0] = tr[p].ch[0];
    tr[tr[q].ch[0]].fa = q;
    tr[p].ch[0] = 0;
    update(p); update(q);
    return res;
}

inline int bottom(int p) {
    splay(p, 0);
    int res = tr[tr[p].ch[0]].siz + 1;
    if(!tr[p].ch[1]) return res;
    if(!tr[p].ch[0]) {
        std::swap(tr[p].ch[0], tr[p].ch[1]);
        return res;
    }
    int q = querypre();
    split(q, tr[q].r);
    splay(q, p);
    tr[q].ch[1] = tr[p].ch[1];
    tr[tr[q].ch[1]].fa = q;
    tr[p].ch[1] = 0;
    update(p); update(q);
    return res;
}

int n, m, op, x, y, ans;

int main() {
    n = readint(); m = readint();
    tot = 1;
    tr[1].l = 1; tr[1].r = n; update(1); spn[1] = 1; rt = 1;
    while(m--) {
        op = readint(); x = readint() - ans; if(op == 1) y = readint() - ans;
        int p;
        if(op != 4) {
            std::map<int, int>::iterator it = --spn.upper_bound(x);
            p = it->second;
            split(p, x);
        }
        if(op == 1) {
            splay(p, 0);
            ans = tr[tr[p].ch[0]].siz + 1;
            spn.erase(tr[p].l);
            tr[p].l = tr[p].r = y; spn[y] = p;
        } else if(op == 2) {
            ans = top(p);
        } else if(op == 3) {
            ans = bottom(p);
        } else {
            ans = queryn(rt, x);
        }
        printf("%d\n", ans);
    }
    return 0;
}


发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据