// 此博文为迁移而来,写于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. Unity依赖注入使用

    构造器注入(Constructor Injection):IoC容器会智能地选择选择和调用适合的构造函数以创建依赖的对象.如果被选择的构造函数具有相应的参数,IoC容器在调用构造函数之前会自定义创建相 ...

  2. RESTful架构入门

    理解RESTful架构 - 阮一峰的网络日志http://www.ruanyifeng.com/blog/2011/09/restful RESTful API 设计指南 - 阮一峰的网络日志http ...

  3. Linux常用命令学习1---(安装、文件系统、目录操作命令cd ls mv cp rm mkdir、链接命令ln……)

    1.理解Linux的文件系统:分区和挂载点    挂载点和路径名无关 /根目录下的/boot完全可以时独立于 /的独立的挂载点,只要你设置就可以    linux安装时候,必须要有这两个分区 / 和 ...

  4. MVC4 遇到问题总结

    1.路径编写: 举例1.<img  src="../Login/VailCode" width="108" height="40"&g ...

  5. NBU expired Media,Media ID not found in EMM database

    Subject:When attempting to expire a media in Veritas NetBackup (tm) 6.0 with the bpexpdate command, ...

  6. phpcms 完美实现 导航栏当前栏目高亮

    我们在用phpcms做网站的时候,经常碰到导航栏高亮功能,或者侧栏高亮,这个会涉及到几个问题: .栏目列表页子栏目高亮判断,如果当前页面为子栏目,他的顶级栏目如果在导航栏也要高亮. .内容页高亮,这个 ...

  7. 腾讯微博的账号登录及api操作

    .tqq.php <?php /** * PHP Library for t.qq.com * * @author */ class tqqPHP { function __construct( ...

  8. Java Security: Illegal key size or default parameters?

    来自:http://stackoverflow.com/questions/6481627/java-security-illegal-key-size-or-default-parameters I ...

  9. 第十八篇:在SOUI中实现PreTranslateMessage

    在MFC中,通常可以通过重载CWnd::PreTranslateMessage这样一个虚函数来实现对一些窗口消息的预处理.多用于tooltip的显示控制. 在SOUI中也实现了类似的机制. 要在SOU ...

  10. linux 用户之间的切换

    从root用户切换到普通用户fxm, 使用如下命令:su - fxm 从普通用户切换到root用户,使用如下命令:su - 或者 su, root可以省略不写.