题目传送门

  转载自https://www.cnblogs.com/yousiki/p/6147455.html,转载请注明出处

  

经典引文

空间效率:O(n)
时间效率:O(log n)插入、查找、删除
创造者:Daniel Sleator 和 Robert Tarjan

优点:每次查询会调整树的结构,使被查询频率高的条目更靠近树根。

Tree Rotation

 
树的旋转是splay的基础,对于二叉查找树来说,树的旋转不破坏查找树的结构。
 

Splaying

 
Splaying是Splay Tree中的基本操作,为了让被查询的条目更接近树根,Splay Tree使用了树的旋转操作,同时保证二叉排序树的性质不变。
Splaying的操作受以下三种因素影响:
  • 节点x是父节点p的左孩子还是右孩子
  • 节点p是不是根节点,如果不是
  • 节点p是父节点g的左孩子还是右孩子
同时有三种基本操作:
 

Zig Step


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

Zig-Zig Step

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

Zig-Zag Step

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

应用

 
Splay Tree可以方便的解决一些区间问题,根据不同形状二叉树中序遍历结果不变的特性,可以将区间按顺序建二叉查找树。
每次自下而上的一套splay都可以将x移动到根节点的位置,利用这个特性,可以方便的利用Lazy的思想进行区间操作。
对于每个节点记录size,代表子树中节点的数目,这样就可以很方便地查找区间中的第k小或第k大元素。
对于一段要处理的区间[x, y],首先splay x-1到root,再splay y+1到root的右孩子,这时root的右孩子的左孩子对应子树就是整个区间。
这样,大部分区间问题都可以很方便的解决,操作同样也适用于一个或多个条目的添加或删除,和区间的移动。

最后附上自己写的洛谷的模板题的代码: 

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
int n,m,root,tot;
struct Node{int ch[2],size;int fa,mark,val;void add(int x,int y){ch[0]=ch[1]=mark=0;val=x;fa=y;size=1;}
}t[N];
inline int read()
{char ch=getchar();int num=0;bool flag=false;while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();}return flag?-num:num;
}
inline void pushup(int x)
{int l=t[x].ch[0],r=t[x].ch[1];t[x].size=t[l].size+t[r].size+1;
}
inline void pushdown(int x)
{if(t[x].mark){t[t[x].ch[0]].mark^=1;t[t[x].ch[1]].mark^=1;swap(t[x].ch[0],t[x].ch[1]);t[x].mark=0;}
}
inline void rotate(int x)
{int y=t[x].fa;int z=t[y].fa;int k=(t[y].ch[1]==x);t[z].ch[t[z].ch[1]==y]=x;t[x].fa=z;t[t[x].ch[k^1]].fa=y;t[y].ch[k]=t[x].ch[k^1];t[x].ch[k^1]=y;t[y].fa=x;pushup(y);pushup(x);
}
inline void splay(int x,int tag)
{while(t[x].fa!=tag){int y=t[x].fa;int z=t[y].fa;if(z!=tag)(t[y].ch[1]==x)^(t[z].ch[1]==y)?rotate(x):rotate(y);rotate(x);}if(tag==0)root=x;
}
inline void insert(int x)
{int now=root,fa=0;while(now)fa=now,now=t[now].ch[x>t[now].val];now=++tot;if(fa)t[fa].ch[x>t[fa].val]=now;t[now].add(x,fa);splay(now,0);
}
inline int find(int x)
{int now=root;while(555){pushdown(now);if(t[t[now].ch[0]].size>=x)now=t[now].ch[0];else if(t[t[now].ch[0]].size+1==x)return now;else x-=(t[t[now].ch[0]].size+1),now=t[now].ch[1];}
}
inline void work(int l,int r)
{l=find(l);r=find(r+2);splay(l,0);splay(r,l);t[t[t[root].ch[1]].ch[0]].mark^=1;
}
inline void print(int x)
{pushdown(x);if(t[x].ch[0])print(t[x].ch[0]);if(t[x].val>1&&t[x].val<n+2)printf("%d ",t[x].val-1);if(t[x].ch[1])print(t[x].ch[1]);
}
int main()
{n=read();m=read();for(int i=1;i<=n+2;i++)insert(i);for(int i=1;i<=m;i++){int l=read();int r=read();work(l,r);}print(root);return 0;
}

转载于:https://www.cnblogs.com/cytus/p/8252154.html

洛谷P3391文艺平衡树(Splay)相关推荐

  1. [洛谷P3391] 文艺平衡树 (Splay模板)

    初识splay 学splay有一段时间了,一直没写...... 本题是splay模板题,维护一个1~n的序列,支持区间翻转(比如1 2 3 4 5 6变成1 2 3 6 5 4),最后输出结果序列. ...

  2. 浅尝无旋Treap (基于洛谷P3391 文艺平衡树)

    说是浅尝吧,确实也挺浅的,完全是基于下面这道题写的↓ 洛谷P3391 自己去看题,我是懒得粘了... 分析 其实也没有什么好分析的,这就是一道Splay树的模板题,解决一般的Treap不能解决的区间维 ...

  3. 洛谷 P3391 文艺平衡树

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

  4. [洛谷P3391]文艺平衡树

    题目 传送门 to luogu 思路 题外话:一开始用Splay\text{Splay}Splay写了一发,结果-- 然后就换成了Treap\text{Treap}Treap.嗯,无旋Treap\te ...

  5. 洛谷 P3391 【模板】文艺平衡树

    题目背景 这是一道经典的Splay模板题--文艺平衡树. 题目描述 您需要写一种数据结构,来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4 ...

  6. BZOJ 3223: Tyvj 1729 文艺平衡树(splay)

    速度居然进前十了...第八... splay, 区间翻转,用一个类似线段树的lazy标记表示是否翻转 ------------------------------------------------- ...

  7. BZOJ 3223: Tyvj 1729 文艺平衡树-Splay树(区间翻转)模板题

    3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 6881  Solved: 4213 [Submit][S ...

  8. 洛谷 - P3391 【模板】文艺平衡树(Splay-区间反转)

    题目链接:点击查看 题目大意:给出一个初始时 a[ i ] = i 的序列,给出 m 次操作,每次操作将区间 [ l , r ] 内的数列反转,问最终的数列是什么 题目分析:Splay处理区间反转问题 ...

  9. P3391 文艺平衡树(Splay区间翻转)

    题解: Splay区间反转的模板题. 翻转[3,5]时,我们可以将3的前驱找到,5的后继找到,然后根节点的右儿子的左子树就是属于[3,5]这个区间的数,可以自己画画就容易知道了. 然后就是给这个结点打 ...

最新文章

  1. 【C】linux下切换工作目录至程序所在目录
  2. C#中的数据类型转换
  3. 出参传递数组指针_C语言指针重难点详解
  4. java中split以“.“ 、“\“、“|”分隔
  5. 【Java】七巧板着色问题
  6. Android 系统性能优化(43)---Android OOM案例分析
  7. 关于求XXX.class.getResource(xxx).getPath()的用法
  8. grundland去色
  9. PHP 汉字转拼音Class类
  10. 【GlobalMapper精品教程】014:矢量线图层的创建及数字化操作
  11. 坐标转换工具类:84坐标系,火星坐标系,与百度坐标系之间的互相转换
  12. Tableau字符串拼接
  13. 服务器搭建网站完整教程
  14. scriptmanager
  15. 求多项式浮点java思路,求多项式函数实数根的方法
  16. CSS3 3D旋转立方
  17. MATLAB仿真斜坡信号的校正,控制系统仿真matlab第六章习题答案.doc
  18. openwrt修改lan口地址失败_wr703n 刷openwrt 网口是lan口,怎样改成wan口?
  19. ESP-WRO0M-32(一):VS Code环境搭建
  20. Excel NORMINV函数用法

热门文章

  1. [java][ide][sts] 使用配置
  2. ubuntu16.04 英文环境安装中文输入法
  3. Android——Handler总结
  4. Invoke()/BeginInvoke()区别
  5. 【精品资源】干货分享:20款精美的手机网站模板下载
  6. Asp.net 服务器端控件
  7. Jmeter-接口测试相关
  8. Linux之压缩与解压缩
  9. MessageBox只弹出一次
  10. android利用WebView实现浏览器的封装