POJ-3468 A Simple Problem with Integers Splay树

该题是一道简单的区间求和题,跟杭电的敌兵布阵很是想象,这里是使用Splay写的。

Splay就是伸展的意思,因为该树在使用过程中会大量的执行Rotate函数,该函数就是在保持二叉树中序遍历不变的情况下,使树的结构发生变化。

该题中,二叉树中存储的值并不一定要求是顺序的,满足左孩子比根节点小,右孩子比根节点大,其大小只是一个构树时的顺序关系。每次旋转能够时确保目标节点一定要在旋转节点的下方,因为任何Rotate操作旋转节点的深度都是在降低的。Splay数的作用在于能够Splay操作选出区间,首先建立两个理论上的无穷小和无穷大点,如果选择区间[a, b],那么将
a-1号节点旋转到根节点,再将b+1号节点旋转到根节点之下,由于b+1 > a-1 恒成立,所以b+1号节点一定是其右孩子,根据中序遍历的关系,a-1之后就是b+1的左子树,再就是b+1了,因此我们就选择出了区间[a, b]。再在上面做一些操作即可。

代码如下:

#include <cstring>

#include <cstdio>

#include <cstdlib>

#define L(x) tree[x].ch[0]

#define R(x) tree[x].ch[1]

#define Key L(R(rt))

#define MAXN 100005

using namespace std;



int N, Q, queue[MAXN], seq[MAXN], rt, front;



struct Node

{

    int di, v, ch[2], lazy, fa;

    long long s;

    void New(int x, int y) {

        di = 1, v = s = x, fa = y;

        lazy = ch[0] = ch[1] = 0;

    }

    void Add(int x) {

        lazy += x;

        s += (long long)di * x;

        v += x;

    }

}tree[MAXN];



void push_up(int x)

{

    tree[x].di = 1+tree[L(x)].di+tree[R(x)].di;

    tree[x].s = tree[x].v+tree[L(x)].s+tree[R(x)].s;

}



void push_down(int x)

{

    if (tree[x].lazy) {

        tree[L(x)].Add(tree[x].lazy);

        tree[R(x)].Add(tree[x].lazy);

        tree[x].lazy = 0;

    }

}



void rotate(int x, int f)

{

    int y = tree[x].fa, z = tree[y].fa;

    push_down(y), push_down(x);

    tree[y].ch[!f] = tree[x].ch[f];

    tree[ tree[y].ch[!f] ].fa = y;

    tree[x].ch[f] = y;

    tree[y].fa = x;

    tree[x].fa = z;

    if (z) {

        tree[z].ch[R(z) == y] = x;

    }  

    push_up(y);

}



void splay(int x, int obj)

{

    int y, z;

    while (tree[x].fa != obj) {

        y = tree[x].fa, z = tree[y].fa;

        if (z == obj) {

            rotate(x, L(y) == x);

        }

        else {

            if (x == L(y) && y == L(z)) {

                rotate(y, 1), rotate(x, 1);

            }

            else if (x == R(y) && y == R(z)) {

                rotate(y, 0), rotate(x, 0);

            }

            else if (x == R(y) && y == L(z)) {

                rotate(x, 0), rotate(x, 1);

            }

            else {

                rotate(x, 1), rotate(x, 0);

            }

        }

    }

    push_up(x);

}



void select(int x, int k, int obj)

{

    if (k != tree[L(x)].di) {

        if (k < tree[L(x)].di) {

            select(L(x), k, obj);

        }

        else {

            select(R(x), k-tree[L(x)].di-1, obj);

        }

    }

    else {

        splay(x, obj);

        if (obj == 0) {

            rt = x;

        }

    }

}



void build(int &x, int l, int r, int fa)

{

    if (l > r)  return;

    int m = (l+r) >> 1;

    x = queue[++front];

    tree[x].New(seq[m], fa);

    build(L(x), l, m-1, x);

    build(R(x), m+1, r, x);

    push_up(x);

}



void init()

{

    for (int i = 0; i < MAXN; ++i) {

        queue[i] = i;

    }

    rt = queue[++front];

    tree[rt].New(0, 0);

    R(rt) = queue[++front];

    tree[R(rt)].New(0, rt);

    build(Key, 1, N, R(rt));  // 构造出两个边界点以及题给的信息区间

//    push_up(R(rt));

//    push_up(rt);

}



void travel(int x)

{

    if (x) {

        travel(L(x));

        printf("di=%d, s=%lld, v=%d, fa.v=%d\n", tree[x].di, tree[x].s, tree[x].v, tree[tree[x].fa].v);

        travel(R(x));

    }

}



int main()

{

    char op[5];

    int x, y, z;

    while (scanf("%d %d", &N, &Q) == 2) {

        for (int i = 1; i <= N; ++i) {

            scanf("%d", &seq[i]);

        }

        init();

        while (Q--) {

            scanf("%s %d %d", op, &x, &y);

            select(rt, x-1, 0);

            select(rt, y+1, rt);

            if (op[0] == 'C') {

                scanf("%d", &z);

                tree[Key].Add(z);

            }

            else {

                printf("%lld\n", tree[Key].s);

            }

        }

    }

    return 0;

}

你可能感兴趣的:(Integer)