splay 是一种很灵活的数据结构。它是动态树的基础,动态树是维护树上数据的神器啊~

splay是一种特殊的二叉查找树,与平衡树不同,不是按照判断高度来降低深度的。
每次插入一个点,或者每次处理一个点,都需要进行一次splay操作把该点移动到根,根据数据的局部性原理,可以使处理变得快一点。

它有以下规则:(p是x的parent节点)

zig

  • 当p为根节点时,进行zip step操作。
  • 当x是p的左孩子时,对x右旋;
  • 当x是p的右孩子时,对x左旋。

zig-zig

  • 当p不是根节点,且x和p不同为左孩子或右孩子时,进行Zig-Zag操作。
  • 当p为左孩子,x为右孩子时,将x左旋后再右旋。
  • 当p为右孩子,x为左孩子时,将x右旋后再左旋。

zig-zag

  • 当p不是根节点,且x和p不同为左孩子或右孩子时,进行Zig-Zag操作。
  • 当p为左孩子,x为右孩子时,将x左旋后再右旋。
  • 当p为右孩子,x为左孩子时,将x右旋后再左旋。

splay可以替代线段树进行更加复杂的区间操作,但是一般如果不是线段树解决不了的问题,尽量不能用splay做,因为splay常数比线段树大很多。

splay的维护懒惰标记也很巧妙,每次旋转的时候先把标记下降,然后旋转,旋转之后只有该节点原来的父亲的数据会发生变化,需要pushup一下,当前节点的数据可以等位置固定了之后再维护。

如果要计算一段区间内的值,可以先把该段位置的前一个节点splay到根,然后把该段位置的后一个节点splay到root,这样的话,后一个的节点的左子树就是该段区间所维护的值了。

下面是一个实现:

题目链接

/***********************************************\
 |Author: YMC
 |Created Time: 2015/3/25 21:34:19
 |File Name: hdu1166.cpp
 |Description: 
\***********************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#define L(rt) (rt<<1)
#define R(rt) (rt<<1|1)
#define mset(l,n) memset(l,n,sizeof(l))
#define rep(i,n) for(int i=0;i<n;++i)
#define maxx(a) memset(a, 0x3f, sizeof(a))
#define zero(a) memset(a, 0, sizeof(a))
#define srep(i,n) for(int i = 1;i <= n;i ++)
#define MP make_pair
const int inf=0x3f3f3f3f ;
const double eps=1e-8 ;
const double pi=acos (-1.0);
typedef long long ll;

using namespace std;
#define maxn 110000
struct Node {
    int val;
    int sum;
    int add;
    Node* ch[2], *f;
    int size;
    void init() {
        f = ch[0] = ch[1] = NULL; size = 1;
        add = 0;    //延迟标记
    }
    void rot(int c) {
        Node *y = f, *z = y->f;
        //y->push_down(); push_down();
        y->ch[!c] = ch[c];  if(ch[c]) ch[c]->f = y;
        f = z;
        if(z) {
            if(y == z->ch[0])   z->ch[0] = this;
            else z->ch[1] = this;
        }
        ch[c] = y;  y->f = this;
        y->update();    //自己暂时不更新,y是原来的fa,在自己下面
    }
    void splay(Node *fa) {
        for(pushdown(); f != fa; ) {    //每次自己要旋转,则需要把标记传递下去,子标记不受影响。
            if(f->f == fa) {
                if(f->ch[0] == this) rot(1);
                else rot(0);
            } else {
                Node *y = f, *z = y->f;
                if(y->ch[0] == this) {
                    if(z->ch[0] == y) {
                        y->rot(1); rot(1);
                    } else {
                        rot(1); rot(0);
                    }
                } else {
                    if(z->ch[0] != y) {
                        y->rot(0); rot(0);
                    } else {
                        rot(0); rot(1);
                    }
                }
            }
        }
        update();   //本节点最后更新
    }
    void pushdown() {
        if(add) {
            if(ch[0]) {
                ch[0]->add += add;
                ch[0]->val += add;
                ch[0]->sum += ch[0]->size * add;
            }
            if(ch[1]) {
                ch[1]->add += add;
                ch[1]->val += add;
                ch[1]->sum += ch[1]->size * add;
            }
            add = 0;
        }
    }

    void update() {
        size = 1;
        if(ch[0]) size += ch[0]->size;
        if(ch[1]) size += ch[1]->size;
        sum = val;
        if(ch[0]) sum += ch[0]->sum;
        if(ch[1]) sum += ch[1]->sum;
    }
    Node* find(int r) {
        pushdown();     //找的时候要维护一些信息。不然找到的节点的信息错误
        int L = 0; if(ch[0]) L += ch[0]->size;
        if(r <= L) return ch[0]->find(r);
        if(r == L+1) return this;
        return ch[1]->find(r-L-1);
    }

}node[maxn];
int e;
inline Node* _alloc() {
    node[e].init(); return &node[e++];
}
int da[maxn];
Node* _make(int l, int r) {
    if(l > r) return NULL;
    int mid = l + r >> 1;   //从中间开始建,树的深度最小
    Node *p = _alloc(); p->val = da[mid];
    p->ch[0] = _make(l, mid-1); if(p->ch[0]) p->ch[0]->f = p;
    p->ch[1] = _make(mid+1, r); if(p->ch[1]) p->ch[1]->f = p;
    p->update();    //子节点更新好之后再更新自己。
    return p;
}
char op[10];
int a, b, c, n;
int main() {
    //freopen("input.txt","r",stdin); 
    int T;
    scanf("%d",&T);
    int cas = 0;
    while(T--) {
        printf("Case %d:\n", ++cas);
        scanf("%d",&n);
        srep(i,n) scanf("%d",&da[i]);
        da[0] = da[n+1] = 0;
        e = 0;
        Node *root = _make(0, n+1), *tp;
        while(scanf("%s",op) != EOF) {
            if(op[0] == 'Q') {
                scanf("%d %d",&a,&b); a++; b++;
                tp = root->find(a - 1);
                tp->splay(NULL); root = tp;
                tp = root->find(b + 1);
                tp->splay(root);
                tp = tp->ch[0];
                printf("%d\n",tp->sum);
            } else if(op[0] == 'A') {
                scanf("%d %d",&a,&b); a++;
                tp = root->find(a-1);
                tp->splay(NULL);    root = tp;
                tp = root->find(a+1);
                tp->splay(root);
                tp = tp->ch[0];
                tp->sum += b;
            } else if(op[0] == 'S') {
                scanf("%d %d",&a,&b); a++;
                tp = root->find(a-1);
                tp->splay(NULL);    root = tp;
                tp = root->find(a+1);
                tp->splay(root);
                tp = tp->ch[0];
                tp->sum -= b;
            } else break;
        }
    }
    return 0;
}