[Splay] POJ3468 A Simple Problem with Integers

一道经典的线段树题目,被我拿来SPLAY练手了,。

熟悉下区间求和,lazy区间更新。

SPLAY的旋转操作真心太强了,从AVL学习了旋转之后迫不及待地学习了SPLAY。

SPLAY原本也是二叉树,我们对SPLAY的层次结构并不关注,只需要注意到任意旋转操作不会改变中序遍历,于是就可以用来维护数列了。

有个技巧就是预先插入两个无关节点,ROOT和ROOT->right_kid。之后把要操作的节点都插入到ROOT->right_kid的左子树,这样就可以确保整个区间都可以操作。

于是对[L,R]区间操作时就可以把L-1对应的节点旋转到根,把R+1对应的节点旋转成根的右儿子,所以对ROOT->right_kid的左子树就是对应整个区间了。

需要注意的是无关节点要真正做到无关,比如我们要维护最小值,那么这两个节点的值要置为INF,否则统计到root可能会出错。

#include<algorithm>
#include<cstdio>
#define lson(x) (kid[x][0])
#define rson(x) (kid[x][1])
#define ll long long int
using namespace std;
const int N = 100005;
int pre[N], size[N], kid[N][2];
ll data[N], sum[N], lazy[N];
int root, cnt;
void NewNode(int &x, int val, int f){
    x = ++cnt;
    pre[x] = f;
    size[x] = 1;
    data[x] = sum[x] = val;
    lazy[x] = lson(x) = rson(x) = 0;
}
void push_up(int x){
    size[x] = size[lson(x)] + size[rson(x)] + 1;
    sum[x] = sum[lson(x)] + sum[rson(x)] + data[x] + lazy[x];
}
void push_down(int x){
    ll &k = lazy[x];
    if(k){
        data[x] += k;
        lazy[lson(x)] += k;
        sum[lson(x)] += k*size[lson(x)];

        lazy[rson(x)] += k;
        sum[rson(x)] += k*size[rson(x)];
        k = 0;
    }
}
void Rotate(int x, int f){
    int y = pre[x];
    push_down(y); push_down(x);
    kid[y][!f] = kid[x][f];
    pre[kid[x][f]] = y;
    if(pre[y]) kid[pre[y]][ kid[pre[y]][1] == y] = x;
    pre[x] = pre[y];
    kid[x][f] = y;
    pre[y] = x;
    push_up(y); //这里不必更新x
}
void Splay(int x, int goal){ //用两个单旋代替双旋偷懒orz, 其实还可以更加偷懒
    push_down(x);
    while(pre[x] != goal){
        if(pre[pre[x]] == goal){
            Rotate(x, kid[pre[x]][0] == x);
        }
        else{
            int y = pre[x], z = pre[y];
            int f = (kid[z][0] == y);
            if(kid[y][f] == x){
                Rotate(x, !f);
                Rotate(x, f);
            }
            else{
                Rotate(y, f);
                Rotate(x, f);
            }
        }
    }
    push_up(x);
    if(goal == 0) root = x;
}
void RotateTo(int k, int goal){
    int x = root;
    push_down(x);
    while(size[lson(x)] != k){
        if(k < size[lson(x)]){
            x = lson(x);
        }
        else{
            k -= (size[lson(x)]+1);
            x = rson(x);
        }
        push_down(x);
    }
    Splay(x, goal);
}
int n, q;
int a[N];
void build(int &x, int l, int r, int f){
    if(l > r) return;
    int m = (l+r) >> 1;
    NewNode(x, a[m], f);
    build(lson(x), l, m-1, x);
    build(rson(x), m+1, r, x);
    push_up(x);
}
void query(int l, int r){
    RotateTo(l-1, 0);
    RotateTo(r+1, root);
    printf("%lld\n", sum[lson(rson(root))]);
}
void update(int l, int r){
    ll tmp;
    scanf("%lld%*c", &tmp);
    RotateTo(l-1, 0);
    RotateTo(r+1, root);
    lazy[lson(rson(root))] += tmp;
    sum[lson(rson(root))] += tmp*size[lson(rson(root))];
}
int main(){
    char p;
    int n1, n2;
    while(scanf("%d %d%*c", &n, &q) != EOF){
        for(int i = 0; i < n; ++i) scanf("%d%*c", &a[i]);
        root = cnt = 0;
        pre[0] = kid[0][0] = kid[0][1] = size[0] = data[0] = lazy[0] = 0;
        sum[0] = 0;
        NewNode(root, -1, 0);  //这里无关节点不会影响数列,因为操作数列时已经将数列旋转到ROOT->right_kid的左子树。
        NewNode(rson(root), -1, root);
        size[root] = 2;
        build(lson(rson(root)), 0, n-1,rson(root));
        push_up(rson(root));
        push_up(root);
        while(q--){
            scanf("%c %d %d%*c", &p, &n1, &n2);
            if(p == 'Q') query(n1, n2);
            else if(p == 'C') update(n1, n2);
        }
        
    }
}

你可能感兴趣的:(数据结构,poj,splay)