zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    win7一关机电脑就蓝屏
    win10系统怎么查自己电脑的IP地址[系统天地]
    Win7如何设置任务管理器的快捷键?[系统天地]
    Win10系统下安全登录密码忘记了怎么办【系统天地】
    Win7系统很卡应该如何解决?[系统天地]
    Win7系统运行慢如何解决?——系统天地
    Win7如何解决开机应用程序无法正常启动
    win10系统怎么切换独立显卡
    程序员的自我修养
    SQL学习笔记
  • 原文地址:https://www.cnblogs.com/little-aztl/p/14198057.html
Copyright © 2011-2022 走看看