poj 3580 SuperMemo 题解

一、题目:

poj原题

二、思路:

很明显这是一道Splay的恶心题。居然还是一场比赛里面的,这出题人也够毒瘤的。

那么相对于很裸的平衡树模板题,这道题的操作无非多了一个REVOLVE。其实也很简单。我们考虑如果将(a[lsim r])旋转(t)次,就等效于将(a[(r-t+1)sim r])放在(a[lsim (r-t)])的前面。所以我们将区间(a[lsim r])单拎出来以后(设该区间在平衡树中的子树为(S)),只需将(a[r-t+1])旋转到(S)的根,然后将(S.root)的左子树(记为(T))与(S.root)切断,并将(T)接到(S.root)的右子树的“最后”一个元素的下面即可。

有一个细节需要注意,是在INSERT操作中。我的方法是将区间(a[xsim x])单拎出来,在(a[x])下面接上新元素即可。但是需要注意的是,接新元素之前,需要将(a[x])的标记全部清除。这是因为如果没有及时清除的话,新元素就会在以后的down中被打上标记,而这显然是不正确的。

三、代码:

//先说明一下,为了避免边界问题,平衡树中会多两个元素+inf和-inf,这样的话每次调用kth函数的时候,要将k加上1。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>

using namespace std;

#define LL long long
#define mem(s, v) memset(s, v, sizeof s)
#define FILEIN(s) freopen(s".in", "r", stdin)
#define FILEOUT(s) freopen(s".out", "w", stdout)

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

const int maxn = 200005;
const int inf = 1e9;

int n;
int a[maxn];

int root, sz;
int son[maxn][2], siz[maxn], fa[maxn];
bool tag[maxn];
int val[maxn], minv[maxn], add[maxn];

inline int get(int x) { return son[fa[x]][1] == x; }

inline void update(int x) {
    siz[x] = siz[son[x][0]] + siz[son[x][1]] + 1;
    minv[x] = val[x];
    if (son[x][0]) minv[x] = min(minv[x], minv[son[x][0]]);
    if (son[x][1]) minv[x] = min(minv[x], minv[son[x][1]]);
}

inline void down(int x) {
    if (tag[x]) {
        swap(son[x][0], son[x][1]);
        tag[son[x][0]] ^= 1; tag[son[x][1]] ^= 1;
        tag[x] = 0; 
    }
    if (add[x]) {
        if (son[x][0]) add[son[x][0]] += add[x], val[son[x][0]] += add[x], minv[son[x][0]] += add[x];
        if (son[x][1]) add[son[x][1]] += add[x], val[son[x][1]] += add[x], minv[son[x][1]] += add[x];
        add[x] = 0;
    }
}

inline int build(int l, int r) {
    if (l > r) return 0;
    int mid = (l + r) >> 1, now = ++sz;
    son[now][0] = build(l, mid - 1); fa[son[now][0]] = now;
    son[now][1] = build(mid + 1, r); fa[son[now][1]] = now;
    val[now] = a[mid];
    update(now);
    return now;
}

inline void rotate(int x) {
    int y = fa[x], z = fa[y], k = get(x);
    down(y); down(x);
    son[y][k] = son[x][k ^ 1]; fa[son[y][k]] = y; fa[y] = x;
    son[x][k ^ 1] = y; fa[x] = z;
    if (z) son[z][son[z][1] == y] = x;
    update(y); update(x);
}

inline void splay(int x, int goal) {
    for (int f; (f = fa[x]) != goal; rotate(x)) {
        if (fa[f] != goal) rotate((get(x) == get(f)) ? f : x);
    }
    if (!goal) root = x;
}

inline int kth(int k) {
    int now = root;
    while (233) {
        down(now);
        if (siz[son[now][0]] >= k) now = son[now][0];
        else {
            k -= siz[son[now][0]];
            if (k == 1) return now;
            --k;
            now = son[now][1];
        }
    }
}

inline int pick(int l, int r) {//单拎区间
    int tmp1 = kth(l), tmp2 = kth(r + 2);
    splay(tmp1, 0); splay(tmp2, tmp1);
    return son[tmp2][0];
}

inline void ADD(int l, int r, LL v) {
    int now = pick(l, r);
    add[now] += v; minv[now] += v; val[now] += v;
    splay(now, 0);
}

inline void REVERSE(int l, int r) {
    int now = pick(l, r);
    tag[now] ^= 1;
    splay(now, 0);
}

inline void INSERT(int x, LL P) {
    int now = pick(x, x);
    down(now);//注意先将标记全部清除
    val[++sz] = P; fa[sz] = now;
    son[now][1] = sz;
    splay(sz, 0);
}

inline void DELETE(int x) {
    int now = pick(x, x);
    now = fa[now];
    son[now][0] = 0; splay(now, 0);
}

inline void MIN(int l, int r) {
    int now = pick(l, r);
    printf("%d
", minv[now]);
}

inline void REVOLVE(int l, int r, int t) {
    t %= (r - l + 1);
    if (t == 0) return;
    int now = pick(l, r);
    now = fa[now];
    splay(kth(r - t + 2), now); 
    now = son[now][0];
    int x = now;
    while (233) {
        down(x);
        if (!son[x][1]) break;
        x = son[x][1];
    }
    int y = son[now][0];
    fa[y] = x; son[x][1] = y;
    son[now][0] = 0;
    splay(y, 0);
}

int main() {
    n = read();
    for (register int i = 1; i <= n; ++i) {
        a[i] = read();
    }
    a[0] = -inf; a[n + 1] = inf;
    root = build(0, n + 1);
    
    string opt; int x, y; LL v;
    int m = read();
    while (m--) {
        cin >> opt;
        if (opt == "ADD") {
            x = read(), y = read(), v = read();
            ADD(x, y, v);
        }
        if (opt == "REVERSE") {
            x = read(); y = read();
            REVERSE(x, y);
        }
        if (opt == "REVOLVE") {
            x = read(), y = read(); v = read();
            REVOLVE(x, y, v);
        }
        if (opt == "INSERT") {
            x = read(); v = read();
            INSERT(x, v);
        }
        if (opt == "DELETE") {
            x = read();
            DELETE(x);
        }
        if (opt == "MIN") {
            x = read(); y = read();
            MIN(x, y);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/little-aztl/p/14198057.html