splay模板(功能最全)

回想起来splay大概是高中时的噩梦吧,手敲splay的确挑战太大了,不过到了acm时期,其实应该是不用惧怕splay的,把板子准备好就问题不大。

poj3580
虽然不是最简单的题,但是可以作为板子题
https://vjudge.net/problem/POJ-3580

题目大意:
要求实现一种数据结构,支持对一个数字序列的 6 种操作:
ADD x y val:第 x 个数到第 y 个数之间的数每个加 D;
REVERSE x y:第 x 个数到第 y 个数之间全部数翻转;
REVOLVE x y c:第 x 个数到第 y 个数之间的数,向后循环流动 c 次,即后面 c
个数变成这段子序列的最前面 c 个,前面的被挤到后面。
INSERT x P:在第 x 个数后面插入一个数 P。
DELETE x:删除第 x 个数。
MIN x y:求第 x 个数到第 y 个数之间的最小数字。

解释一下我的模板,首先有一
个很重要的操作:提取区间,要操作[a,b]区间,那么将 a-1 先旋转到根节点,
再将 b+1 旋转到根节点的右子节点,那么由中序遍历的性质可以知道,此时根
节点的右子节点的左子树是我们要操作的区间。因此在提取了操作的区间以
后:
1、对于区间加的操作, 只需要加上一个加标记,并且修改这个节点维护的区间
最值信息;
2、对于区间翻转操作,只需要加上一个翻转标记;
3、对于区间插入操作,如果插入到 pos,那么我们可以选择区间[pos+1,pos]
(对,大的在前)也就是把 pos 旋到根,把 pos+1 旋到根的右子结点,并且此
时根的右子结点的左子树是空的,然后插入到根的右子结点的左子结点上(这
个操作也可以把砍下来的一棵子树重新拼接上去);
4、对于删除结点操作,只需将该结点放入回收队列中,并且修改根的右子结点
的信息即可(砍树的时候只需要断开父子关系并且修改父亲结点信息);
5、对于求区间最值操作,由于每一个节点本身已经维护了一个子树信息,直接
提取即可;
6、对于区间移位操作,在右移之后,可以发现[a,b]被分为两个区间[a,bc] [b-c+1,b],将后者插入到前者之前即可( c 可能很大,需要先取模)。
所有操作除了翻转以外都需要先把延迟标记推下来,否则就会更新不及时而出
错,具体见代码。
另外,由于内存占用比较多,所以我加上了一个内存回收队列,把删掉的节点
放入到那里去,然后插入新节点的时候先看回收队列中有无可用节点,没有的
话再向内存池申请。
手调过的板子:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#define rep(i) for (int i=0; i<n; i++)
using namespace std;
typedef long long ll;
const int N=200005, inf=0x3f3f3f3f;

typedef struct splaynode* node;
struct splaynode {
    node pre, ch[2];
    int value, lazy, min;
    int size, rev;
    void init(int _value) {
        pre=ch[0]=ch[1]=NULL;
        min=value=_value;
        lazy=rev=0;
        size=1;
    }
}mem[N];
int memtop;

stack<node> S;
node root;

inline int getsize(node &x) {
    return x?x->size:0;
}

void pushdown(node &x) {
    if (!x) return;
    if (x->lazy) {
        int w = x->lazy;
        x->value += w;
        if (x->ch[0]) {
            x->ch[0]->lazy += w;
            x->ch[0]->min += w;
        }
        if (x->ch[1]) {
            x->ch[1]->lazy += w;
            x->ch[1]->min += w;
        }
        x->lazy = 0;
    }
    if (x->rev) {
        node t = x->ch[0];
        x->ch[0] = x->ch[1];
        x->ch[1] = t;
        x->rev = 0;
        if (x->ch[0]) x->ch[0]->rev ^= 1;
        if (x->ch[1]) x->ch[1]->rev ^= 1;
    }
}

void update(node &x) {
    if (!x) return;
    x->size = 1;
    x->min = x->value;
    if (x->ch[0]) {
        x->min = min(x->min, x->ch[0]->min);
        x->size += x->ch[0]->size;
    }
    if (x->ch[1]) {
        x->min = min(x->min, x->ch[1]->min);
        x->size += x->ch[1]->size;
    }
}

void rotate(node &x, int d) {
    node y = x->pre;
    pushdown(y);
    pushdown(x);
    pushdown(x->ch[d]);
    y->ch[!d] = x->ch[d];
    if (x->ch[d] != NULL) x->ch[d]->pre = y;
    x->pre = y->pre;
    if (y->pre != NULL)
        if (y->pre->ch[0] == y) y->pre->ch[0] = x; else y->pre->ch[1] = x;
    x->ch[d] = y;
    y->pre = x;
    update(y);
    if (y == root) root = x;
}

void splay(node &src, node &dst) {
    pushdown(src);
    while (src!=dst) {
        if (src->pre=
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值