序列终结者

Description

BZOJ1251
Luogu4146
维护一个序列,支持区间加,区间翻转,区间取最大值这三个操作。

Solution

Splay.
就是细节比较麻烦。

Code

#include <cstdio>
#include <algorithm>
 
const int N = 50000 + 10;
 
int fa[N], ch[N][2], val[N], sz[N], rev[N], add[N], mx[N];
 
void upd(int x) {
    sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
    mx[x] = std::max(val[x], std::max(mx[ch[x][0]], mx[ch[x][1]]));
}
void pd(int x) {
    if (rev[x]) {
        std::swap(ch[x][0], ch[x][1]);
        rev[ch[x][0]] ^= 1;
        rev[ch[x][1]] ^= 1;
        rev[x] = 0;
    }
    if (add[x]) {
        if (ch[x][0]) val[ch[x][0]] += add[x], add[ch[x][0]] += add[x], mx[ch[x][0]] += add[x];
        if (ch[x][1]) val[ch[x][1]] += add[x], add[ch[x][1]] += add[x], mx[ch[x][1]] += add[x];
        add[x] = 0;
    }
}
int dir(int x) { return x == ch[fa[x]][1]; }
void rot(int x) {
    int f = fa[x], d = dir(x);
    if (fa[x] = fa[f]) ch[fa[x]][dir(f)] = x;
    if (ch[f][d] = ch[x][d^1]) fa[ch[f][d]] = f;
    ch[fa[f] = x][d^1] = f;
    upd(f);
    upd(x);
}
int st[N];
void splay(int x, int t = 0) {
    int top = 0, i;
    for (i = x; fa[i]; i = fa[i]) st[top++] = fa[i];
    while (top--) pd(st[top]);
    pd(x);
    for (; fa[x] != t; rot(x)) if (fa[fa[x]] != t) rot(dir(fa[x]) == dir(x) ? fa[x] : x);
}
int kth(int k, int t) {
    int o = t; 
    while (1) {
        pd(o);
        if (sz[ch[o][0]] == k-1) break;
        else if (sz[ch[o][0]] >= k) o = ch[o][0];
        else { k -= sz[ch[o][0]] + 1; o = ch[o][1]; }
    }
    splay(o, fa[t]);
    return o;
}
int n;
void reverse(int l, int r) {
    splay(1);
    int y = kth(r+1, 1);
    int x = ch[y][0];
    kth(l-1, x);
    rev[ch[ch[y][0]][1]] ^= 1;
}
void addval(int l, int r, int k) {
    splay(1);
     
    int y = kth(r+1, 1);
    int x = ch[y][0];
    kth(l-1, x); 
    add[ch[ch[y][0]][1]] += k;
    val[ch[ch[y][0]][1]] += k;
    mx[ch[ch[y][0]][1]] += k;
}
int query(int l, int r) {
    splay(1);
    int y = kth(r+1, 1);
    int x = ch[y][0];
    kth(l-1, x);
    return mx[ch[ch[y][0]][1]];
}
 
void put(int o) {
    if (!o) return;
    put(ch[o][0]);
    printf("%d %d %d
", ch[o][0], ch[o][1], val[o]);
    put(ch[o][1]); 
}
 
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n+2; ++i) {
        if (fa[i] = i-1) ch[i-1][1] = i;
        sz[i] = n - i + 3;
        val[i] = 0;
    }
    mx[0] = -2147483647;
    val[n+1] = 0;
    int m, l, r, v, op;
    scanf("%d", &m);
    while (m--) {
        scanf("%d%d%d", &op, &l, &r);
        if (op == 1) { scanf("%d", &v); addval(l+1, r+1, v); }
        else if (op == 2) reverse(l+1, r+1);
        else printf("%d
", query(l+1, r+1));
    }
    return 0;
}

Note

如果不是非常必要的话,还是不要使用平衡树了,实在是有点难调,也没有线段树直观。

原文地址:https://www.cnblogs.com/wyxwyx/p/bzoj1251.html