// 此博文为迁移而来,写于2015年7月18日,不代表本人现在的观点与看法。原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w6rg.html

1、前言
       这玩意儿真的搞了我好久,当然前一阵子一直都没有去管它,最近直接参照了YML(@YMDragon)的程序,感觉还是不错的,大概看得懂了,就是逻辑关系有点扯。好,废话不多说……
 
2、概念
       平衡树的全称为:平衡二叉搜索树,功能很强大,带来的后果就是代码非常复杂。首先大家应该都知道二叉搜索树是什么了,那么前面加一个平衡的含义在于什么?根据读入顺序 { 80,30,90,20,40,35,38 },我们先构造出一棵二叉搜索树,如图:

      显然,这棵树长得有点畸形,其根节点左儿子部分节点明显过多。然而这不仅仅是看起来不舒服的问题,但我们要在树上进行某项询问或是某项操作的时候,如果深度过大,将会明显影响到时间效率,可以举一个极端的例子——如果我们构造出的是一条链,那么时间复杂度可想而知。而平衡的含义就出现了,让这棵树长得更像一棵树!在操作的时候能够提高效率。写平衡树的方式有很多种,如:Splay,Treap,红黑树和AVL树等等。Splay相比之下功能强大,代码量较大,今天的重点就在Splay。
 
3、Let's Splay
       Splay的中文是伸展,并不清楚这有什么关系。Splay主要存在三种操作,下面一一介绍:
       1.Zig操作
      目标节点X是根节点的左(右)儿子,则将目标节点右(左)旋转至根节点。

       2.Zig-zig操作
      目标节点X不是根节点的子节点,且为其父节点的左(右)儿子,其父节点为其爷爷节点的左(右)儿子,则将目标节点X和其父节点先后进行右(左)旋。

3.Zig-zag操作

       目标节点X不是根节点的子节点,且为其父节点的左(右)儿子,其父节点为其爷爷节点的右(左)儿子,则将目标节点X进行右(左)旋,其父节点进行左(右)旋。
       具体操作的话,旋转多为自下而上的递归旋转,直到把目标节点旋转至根节点。

4、应用
       平衡树存在一个共同作用也就是前文所提——平衡(废话呢)。但是今天的主题是Splay,之前也说过了它功能强大,那么强大在哪里?第一眼看题目你也许根本想不到要用Splay。现在我们来看一道神题:

      首先这是一道NOI2005的原题,BZOJ上没有给树数据范围,但是我想模拟的时间复杂度和空间复杂度一定是不能承受的。我也曾想到了链,但是感觉实现起来有点麻烦,那么这道题要用到的就是Splay。注意到,六大操作中,全部区间修改或区间询问,故我们先设当前操作对应区间为[x,y](INSERT是个例外,可以认为y=x+1),首先我们将x旋转至根,然后在将y旋转至根,仔细思考一下,[x,y]目前在树中是如何分布的?必然是在同一棵子树之中。那么这样就好处理了,插入时即可类似于在这棵子树中构造(同等于最开始的输入环节),删除时直接将一棵子树清空(清空的具体操作:权值记为0,作标记;可能会遇到一个问题,如果删除操作过多,可能会造成过多的空间资源的浪费,这里我们可以考虑开一个队列,以便以后新插入节点时可以直接使用已经被删除的空间),修改权值和翻转的操作有点类似于线段树;询问的话,提前对于每一棵子树进行维护即可。
 
代码:
----------------------------------------------------------------------------------------------------
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
 
#define MAXN 500005
#define INF 1<<30
using namespace std;
 
struct Node
{
        int son[2],l,r,m,k,t1,t2,s,sum,fa;
};
 
Node tree[MAXN];
int m,n,root,a[MAXN];
char now[10];
queue Q;
 
void plus1(int now,int val)
{
        if (!now) return;
        tree[now].k=val,tree[now].t1=1,tree[now].sum=tree[now].k*tree[now].s;
        if (tree[now].k>0) tree[now].l=tree[now].r=tree[now].m=tree[now].sum; 
        else tree[now].l=tree[now].r=tree[now].m=tree[now].k;
}
 
void plus2(int now)
{
        if (!now) return;
        swap(tree[now].son[0],tree[now].son[1]),tree[now].t2^=1;
        swap(tree[now].l,tree[now].r);
}
 
void push(int now)
{
        if (!now) return;
        if (tree[now].t1) plus1(tree[now].son[0],tree[now].k),plus1(tree[now].son[1],tree[now].k),tree[now].t1=0;
        if (tree[now].t2) plus2(tree[now].son[0]),plus2(tree[now].son[1]),tree[now].t2=0;
}
 
void update(int now)
{
        if (!now) return;
        push(now);
        int lson=tree[now].son[0],rson=tree[now].son[1];
        tree[now].s=tree[lson].s+tree[rson].s+1;
        tree[now].sum=tree[lson].sum+tree[rson].sum+tree[now].k;
        tree[now].l=max(tree[lson].l,tree[lson].sum+tree[now].k+max(tree[rson].l,0));
        tree[now].r=max(tree[rson].r,tree[rson].sum+tree[now].k+max(tree[lson].r,0)); 
        tree[now].m=max(max(tree[lson].m,tree[rson].m),tree[now].k+max(tree[lson].r,0)+max(tree[rson].l,0));
}
 
void rotate(int now)
{
        int fa=tree[now].fa,gfa=tree[fa].fa,q=(tree[fa].son[1]==now),son=tree[now].son[q^1];
        if (gfa) tree[now].fa=gfa,tree[gfa].son[tree[gfa].son[1]==fa]=now;
        else tree[now].fa=0,root=now;
        tree[fa].son[q]=son; if (son) tree[son].fa=fa;
        tree[fa].fa=now,tree[now].son[q^1]=fa;
        update(fa),update(now);
//  让我来翻译一下这段话:
// 新建变量:我的爸爸,我爸爸的爸爸,“我是我爸的右儿子”判断,我的左(右)儿子;
// 如果我有爷爷,那么我的爸爸是我的爷爷,我的爷爷的儿子是我,左还是右取决于我爸爸是我爷爷的左还是右儿子;
// 如果我没爷爷,那么我爸爸也没有了,我就是你们的祖宗
// 我爸爸的儿子是我的儿子;如果我有儿子,那么我儿子的爸爸是我的爸爸
//  我爸爸的爸爸是我,我的左(右)儿子是我爸爸。
}
 
void splay(int now)
{
        int k=0;
        for (int x=now;x;x=tree[x].fa) { k++; a[k]=x; }
        for (int i=k;i>=1;i--) push(a[i]);
        while (tree[now].fa) rotate(now);
}
 
void build(int now,int l,int r)
{
        int mid=(l+r)>>1;
        if (l<mid) tree[now].son[0]=Q.front(),Q.pop(),build(tree[now].son[0],l,mid-1),tree[tree[now].son[0]].fa=now;
        scanf("%d",&tree[now].k);
        if (mid<r) tree[now].son[1]=Q.front(),Q.pop(),build(tree[now].son[1],mid+1,r),tree[tree[now].son[1]].fa=now;
        update(now);
}
        
int rank(int q)
{
        int now=root;
        while (now)
        {
                push(now);
                if (tree[tree[now].son[0]].s+1==q) return now;
                if (tree[tree[now].son[0]].s>=q) now=tree[now].son[0];
                else q-=tree[tree[now].son[0]].s+1,now=tree[now].son[1];
        }
        return 0;
}
 
void ins()
{
        int pos,tot,l,r,root; scanf("%d %d",&pos,&tot);
        l=rank(pos),r=rank(pos+1); splay(l),splay(r);
        root=Q.front(),Q.pop(),build(root,1,tot);
        if (l) tree[l].son[1]=root,tree[root].fa=l;
        else   tree[r].son[0]=root,tree[root].fa=r;
        update(l),update(r);
}
 
void DFS(int now)
{
        if (tree[now].son[0]) DFS(tree[now].son[0]);
        if (tree[now].son[1]) DFS(tree[now].son[1]);
        Q.push(now),memset(tree+now,0,sizeof(tree[now]));
}
 
void del()
{
        int pos,tot,l,r; scanf("%d %d",&pos,&tot);
        l=rank(pos-1),r=rank(pos+tot); splay(l),splay(r);
        if (l) DFS(tree[l].son[1]),tree[l].son[1]=0;
        else   DFS(tree[r].son[0]),tree[r].son[0]=0;
        update(l),update(r);
}
 
void makeSame()
{
        int pos,tot,c,l,r; scanf("%d %d %d",&pos,&tot,&c);
        l=rank(pos-1),r=rank(pos+tot); splay(l),splay(r);
        if (l) plus1(tree[l].son[1],c); else plus1(tree[r].son[0],c);
        update(l),update(r);
}
 
void reverse()
{
        int pos,tot,l,r; scanf("%d %d",&pos,&tot);
        l=rank(pos-1),r=rank(pos+tot); splay(l),splay(r);
        if (l) plus2(tree[l].son[1]); else if (r) plus2(tree[r].son[0]); else plus2(root);
        update(l),update(r);
}
 
int getSum()
{
        int pos,tot,l,r; scanf("%d %d",&pos,&tot);
        l=rank(pos-1),r=rank(pos+tot); splay(l),splay(r);
        if (l) return tree[tree[l].son[1]].sum;
        else return tree[tree[r].son[0]].sum;
}
        
int maxSum() { return tree[root].m; }
 
void init()
{
        freopen("1500.in","r",stdin);
        freopen("1500.out","w",stdout);
        scanf("%d %d",&n,&m);
        for (int i=1;i<=MAXN;i++) Q.push(i);
        tree[0].l=tree[0].r=tree[0].m=-INF;
        root=Q.front(),Q.pop(),build(root,1,n);
}
 
int main()
{
        init();
        for (int i=1;i<=m;i++)
        {
                scanf("%s",&now);
                if (now[0]=='I') ins();
                else if (now[0]=='D') del();
                else if (now[2]=='K') makeSame(); 
                else if (now[0]=='R') reverse();
                else if (now[0]=='G') printf("%d\n",getSum());
                else printf("%d\n",maxSum());
        }
        return 0;
}
---------------------------------------------------------------------------------------------------

[知识点]平衡树之Splay的更多相关文章

  1. P3391 【模板】文艺平衡树(Splay)新板子

    P3391 [模板]文艺平衡树(Splay) 题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转 ...

  2. fhq_treap || BZOJ 3223: Tyvj 1729 文艺平衡树 || Luogu P3391 【模板】文艺平衡树(Splay)

    题面: [模板]文艺平衡树(Splay) 题解:无 代码: #include<cstdio> #include<cstring> #include<iostream> ...

  3. 【BZOJ】3223: Tyvj 1729 文艺平衡树(splay)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3223 默默的.. #include <cstdio> #include <cstr ...

  4. 平衡树(Splay):Splaytree POJ 3580 SuperMemo

    SuperMemo         Description Your friend, Jackson is invited to a TV show called SuperMemo in which ...

  5. bzoj3223 Tyvj 1729 文艺平衡树(Splay Tree+区间翻转)

    3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2202  Solved: 1226[Submit][Sta ...

  6. BZOJ3224普通平衡树【Splay】

    3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 11751  Solved: 5013 Descriptio ...

  7. 【BZOJ3223】文艺平衡树(Splay)

    题面 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 ...

  8. BZOJ 3224 - 普通平衡树 - [Treap][Splay]

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3224 Description 您需要写一种数据结构(可参考题目标题),来维护一些数,其中 ...

  9. P3369 【模板】普通平衡树(splay)

    P3369 [模板]普通平衡树 就是不用treap splay板子,好好背吧TAT #include<iostream> #include<cstdio> #include&l ...

随机推荐

  1. JQuery阻止冒泡事件on绑定中异常情况分析

    科普下事件冒泡以及默认行为,以下面例子举列子:     事件冒泡:当点击内部button元素时,会触发自身及外层 a的点击事件,这就是事件冒泡引起的.事件会随着 DOM 的层次结构依次向上传播. 事件 ...

  2. Linux LVM全面实践

    1.磁盘分区 [root@ol6-121-rac1 ~]# fdisk /dev/sdb Device contains neither a valid DOS partition table, no ...

  3. 1080P、720P、4CIF、CIF所需要的理论带宽

    转自:http://blog.sina.com.cn/s/blog_64684bf30101hdl7.html 在视频监控系统中,对存储空间容量的大小需求是与画面质量的高低.及视频线路等都有很大关系. ...

  4. [liferay6.2]input-date日期控件

    input-date日期控件 liferay6.2中默认提供了一个简单的日期控件input-date,调用代码片段如下: <% Calendar calendar = Calendar.getI ...

  5. Hbase系统架构

    HBase 系统架构 HBase是Apache Hadoop的数据库,能够对大型数据提供随机.实时的读写访问.HBase的目标是存储并处理大型的数据.HBase是一个开源的,分布式的,多版本的,面向列 ...

  6. SQLServer批量创建有规则的数据

    根据需求要生成从kkk001,kkk002开始的100个递增账号 手插要死人的,用SQL脚本轻松完成: declare @a int ) ) begin ) ) end declare:申明变量, @ ...

  7. loadrunner参数化excel数据

    LR参数化数据源Oracle,MSSQL,Excel参数化的方法: 重点介绍excel数据参数化的方法: 1.首先创建excel表格: 注意要写列明   2.创建excel表连接:   参数化完成后, ...

  8. 改了哪里vs调试直接用iis运行

    OAS2Web.csproj 中的useiis为true,但必须要先配置好iis网站指向文件目录  

  9. PHP学习之字符串

    1.字符串变量用于存储并处理文本: 2.字符串变量用于包含有字符的值,在创建字符串后,就可以对它进行操作了,可以直接在函数中使用字符串,或把它存储在变量中: <?php $txt="H ...

  10. 《DSP using MATLAB》示例Example4.5

    代码: x1 = [1, 2, 3]; x2 = [2, 4, 3, 5]; % x1 x2 sequences % n1 = 0:1:2; n2 = 0:1:3; n1 = -1:1:1; n2 = ...