splay树 hdu1166 敌兵布阵

传送门:点击打开链接

题意:....省略100字。。

思路:搞了两天的spay,终于明白了这个算法的优势以及用途和原理了。。。

首先是作用:splay是介于线段树和平衡树之间的,是一种平衡树,既能用来做set,也能用来做priority_queue,还能用来做线段树的一种万能树型数据结构!

与平衡树相比:与BST相比,BST的复杂度会退化,splay的复杂度平摊后是nlogn。与红黑树和SBT相比,在代码的编写上splay要简单一些,但是肯定比红黑树和BST稍微慢一点。

与线段树相比:首先基本能完成线段树的所有操作。但是,常数比线段树大。

然而也有线段树实现不了的功能,这才是我们对于一道题目为什么要选择splay这个数据结构的最重要的原因:

1.能实现区间插入。2.能实现区间删除。3.能实现区间翻转。

这3个是splay相比线段树独一无二的,这决定了splay巨大的价值!


splay网上的资料非常多,,我就说下我觉得容易混淆的地方。。

1.我这个模板的Rotate函数,c=0表示右旋,c=1表示左旋。

2.不要因为这个模板看起来比较短,,就怀疑它只是单旋,这个模板真的有双旋的。复杂度不会退化的放心。。

3.在Prepare函数里,为什么一开始要建立两个Node,这两个Node是端点,相当与第0个和第n+1个

4.为什么要在Prepare的最后Splay(3, 0),这是因为要把内容push_up到第1个节点和第2个节点上去。

5.如果想直接在Build里面读数值的时候,要注意scanf的位置,应该在两个Build的中间,因为splay是中序遍历才是原序列

6.为什么在求区间和的时候,要先Splay(l - 1, 0), 然后再Splay(r + 1, root),因为这样旋转后,son[son[root][1]][0]这棵树,就代表了[l, r]

7.splay的复杂度是平摊的,也就是说它并不是一颗完全二叉树,它可能第一次查询的时候接近O(n),但是后面查询又会非常快,这和并查集非常类似。

8.splay具有堆的性质,根节点大于等于左边树所有的节点,小于等于右边树所有的节点。因为这个性质,可以做到快速找第k大。

9.splay最重要的一个函数就是Splay函数,Splay(a, b)代表把节点a提到b的下面,如果b=0表示把a提到树根 ,所有的操作都是基于Splay函数的

10.splay有很多种旋转,这些旋转都是不会改变堆的性质的。

11.为什么网上经常有单旋和双旋的说法,单旋复杂度可能是会退化的,所以我们一般splay都是写双旋的,其实也并没有那么复杂,代码也不会特别长。

12.Select函数是用来找第k大的函数,然后有人会觉得里面判断向左和向右的那个数值好像有点不对劲,好像特意多算了1个数,那是因为我代码里在NewNode的时候,边界也算了一个size,所以那个多出来的1是为了平衡边界的那个节点的。

13.splay的功能很多基于Select,而它基于维护size,所以一般的splay都需要维护size

14.在Splay函数运行的同时,因为会改变树的结构,所以要动态的push_up和push_down,动态的维护节点的含义

15.基于区间的操作,通常就是用Select按照第6点所说的,搞出一个一个树,代表要操作的区间,然后再打懒惰标记,或者查询,或者删除之类的。


以上都是我学习完splay后的一些见解,如果有不对的地方,还请指出,谢谢拉~

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;

const int MX = 5e4 + 5;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int sum[MX], num[MX], size[MX];
int son[MX][2], fa[MX], root, sz;
void Link(int x, int y, int c) {
    fa[x] = y; son[y][c] = x;
}
void push_up(int rt) {
    size[rt] = size[son[rt][0]] + size[son[rt][1]] + 1;
    sum[rt] = sum[son[rt][0]] + sum[son[rt][1]] + num[rt];
}
void Rotate(int x, int c) {
    int y = fa[x];
    Link(x, fa[y], son[fa[y]][1] == y);
    Link(son[x][!c], y, c);
    Link(y, x, !c);
    push_up(y);
}
void Splay(int x, int g) {
    while(fa[x] != g) {
        int y = fa[x], cx = son[y][1] == x, cy = son[fa[y]][1] == y;
        if(fa[y] == g) Rotate(x, cx);
        else {
            if(cx == cy) Rotate(y, cy);
            else Rotate(x, cx);
            Rotate(x, cy);
        }
    }
    push_up(x);
    if(!g) root = x;
}
void NewNode(int f, int &rt) {
    rt = ++sz;
    fa[rt] = f, size[rt] = 1;
    son[rt][0] = son[rt][1] = 0;
    num[rt] = sum[rt] = 0;
}
int Select(int k, int g) {
    int rt = root;
    while(size[son[rt][0]] != k) {
        if(size[son[rt][0]] > k) rt = son[rt][0];
        else k -= size[son[rt][0]] + 1, rt = son[rt][1];
    }
    Splay(rt, g);
    return rt;
}
void Build(int l, int r, int &rt, int f) {
    if(l > r) return;
    int m = (l + r) >> 1, t;
    NewNode(f, rt);
    Build(l, m - 1, son[rt][0], rt);
    scanf("%d", &t);
    num[rt] = sum[rt] = t;
    Build(m + 1, r, son[rt][1], rt);
    push_up(rt);
}
void Prepare(int n) {
    sz = 0;
    NewNode(0, root);
    NewNode(root, son[root][1]);
    Build(1, n, son[2][0], 2);
    Splay(3, 0);
}
int Query(int l, int r) {
    Select(l - 1, 0);
    Select(r + 1, root);
    return sum[son[son[root][1]][0]];
}
int Update(int p, int s) {
    Select(p, 0);
    sum[root] += s; num[root] += s;
}

int main() {
    int T, ansk = 0, n; //FIN;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        Prepare(n);

        char op[10];
        printf("Case %d:\n", ++ansk);
        while(scanf("%s", op), op[0] != 'E') {
            int a, b;
            scanf("%d%d", &a, &b);
            if(op[0] == 'Q') printf("%d\n", Query(a, b));
            else if(op[0] == 'A') Update(a, b);
            else Update(a, -b);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值