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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值