原题P3391 【模板】文艺平衡树(Splay)

题目背景
这是一道经典的Splay模板题——文艺平衡树。 题目描述
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 ,翻转区间是[,]的话,结果是5 输入输出格式
输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(,,⋯n−,n) m表示翻转操作次数 接下来m行每行两个数 [l,r] 数据保证1≤l≤r≤n 输出格式: 输出一行n个数字,表示原始序列经过m次变换后的结果 输入输出样例
输入样例#: 复制 输出样例#: 复制 说明
n,m≤

查看题面

题解

这道题目要求支持区间翻转,这是splay的典型操作。

基本思想:

  • 只要将一棵BST每一个节点的左右子树交换,就相当于翻转次树的中序遍历

实现方法:

1. 用节点中序遍历的序号表示当前节点的下标,用key存节点的序号,经过一系列操作后中序遍历即为所求

2. 每次翻转区间【L,R】时只要将排名为L-1的点splay到根,将排名R+1的点splay到根的右节点,根的右子树的左子树的中序遍历就是【L,R】

如何将一个点splay到指定位置?

只要引入y标记,当前节点的父亲为y时跳出,就可以将x刚好splay到y的儿子

inline void splay(int x,int y){
for(int fa;(fa=f[x])!=y;rotate(x)){
if(f[fa]!=y){
rotate(get(fa)==get(x)?fa:x);
}
}
if(y==){
root=x;
}
}

1.标记是在每一次访问到一个新的节点是就要pushdown的(改变树的结构会破坏标记区间,所以先一步下传标记)

inline void push_down(int x){
if(x&&tag[x]){
tag[son[x][]]^=;
tag[son[x][]]^=;
swap(son[x][],son[x][]);
tag[x]=;
}
}

2.区分一个节点的排名和这个节点的值:这个节点的排名是它是当前数组中的第几个,用左儿子的size+1表示;这个节点的值是题目中输入的数字,在本题中是1~N

3.增加数字为1和N+2的两个哨兵节点+INF和-INF,因此第i个节点的下标为i+1。

4.难道交换左右子树不会破坏BST的性质吗?这就是容易混淆的一点,我们的区间操作是根据下标翻转的,用数组实现时下标就是数组地址,子树交换时,只是存储内容的改变,下标位置(树的形状)只会在旋转时改变,保证BST性质。

5.此题要求一次性插入N个节点,所以可以用线段树建树的方法O(N)建树

int init(int l,int r,int fa){
if(l>r){
return ;
}
int mid=(l+r)>>,cur=++sz;
key[cur]=val[mid];
f[cur]=fa;
son[cur][]=init(l,mid-,cur);
son[cur][]=init(mid+,r,cur);
upd(cur);
return cur;
}

完整代码

 #include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+,MAXN=1e5+,MAXM=1e5+;
int N,M,root,sz;
int key[MAXN],val[MAXN],f[MAXN],son[MAXN][],tag[MAXN],siz[MAXN];
inline void DEBUG(){
printf("root=%d siz=%d\n",root,sz);
for(int i=;i<=sz;i++){
printf("(idx=%d\tkey=%d\tlc=%d\trc=%d)\n",i,key[i],son[i][],son[i][]);
}
puts("-------------------------------------------------------");
}
inline void upd(int x){
if(x){
siz[x]=;
if(son[x][]){
siz[x]+=siz[son[x][]];
}
if(son[x][]){
siz[x]+=siz[son[x][]];
}
}
}
inline void push_down(int x){
if(x&&tag[x]){
tag[son[x][]]^=;
tag[son[x][]]^=;
swap(son[x][],son[x][]);
tag[x]=;
}
}
inline int get(int x){
return x==son[f[x]][];
}
inline void rotate(int x){
int fa=f[x],gf=f[fa],which=get(x);
son[fa][which]=son[x][which^];
f[son[fa][which]]=fa;
son[x][which^]=fa;
f[fa]=x;
f[x]=gf;
if(gf){
son[gf][son[gf][]==fa]=x;
}
upd(fa);
upd(x);
}
inline void splay(int x,int y){
for(int fa;(fa=f[x])!=y;rotate(x)){
if(f[fa]!=y){
rotate(get(fa)==get(x)?fa:x);
}
}
if(y==){
root=x;
}
}
int init(int l,int r,int fa){
if(l>r){
return ;
}
int mid=(l+r)>>,cur=++sz;
key[cur]=val[mid];
f[cur]=fa;
son[cur][]=init(l,mid-,cur);
son[cur][]=init(mid+,r,cur);
upd(cur);
return cur;
}
inline int findx(int x){//return the index
int cur=root;
while(){
push_down(cur);
if(son[cur][]&&x<=siz[son[cur][]]){
cur=son[cur][];
}else{
x-=siz[son[cur][]]+;
if(!x){
return cur;
}
cur=son[cur][];
}
}
}
void dfs(int x){
push_down(x);
if(son[x][]){
dfs(son[x][]);
}
if(key[x]!=INF&&key[x]!=-INF){
printf("%d ",key[x]);
}
if(son[x][]){
dfs(son[x][]);
}
}
int main(){
scanf("%d%d",&N,&M);
for(int i=;i<=N+;i++){
val[i]=i-;
}
val[]=-INF;
val[N+]=INF;
root=init(,N+,);
for(int i=;i<=M;i++){
int ii,jj;
scanf("%d%d",&ii,&jj);
int l=findx(ii),r=findx(jj+);
splay(l,);
splay(r,l);
tag[son[son[root][]][]]^=;
}
dfs(root);
return ;
}

splay区间翻转的更多相关文章

  1. hdu-1890-Robotic Sort splay区间翻转

    题意: 依次找第i大的数下标pos[i],然后将区间[i,pos[i]]翻转 分析: splay树区间翻转 // File Name: ACM/HDU/1890.cpp // Author: Zlbi ...

  2. bzoj 3223 文艺平衡树 splay 区间翻转

    Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 17715  Solved: 7769[Submit][Status][ ...

  3. bzoj 1251序列终结者 splay 区间翻转,最值,区间更新

    序列终结者 Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 4594  Solved: 1939[Submit][Status][Discuss] De ...

  4. BZOJ 3223 Splay区间翻转

    思路: 区间翻转的裸题 终于tm理解splay了-- //By SiriusRen #include <cstdio> #include <cstring> #include ...

  5. Splay(区间翻转)&树套树(Splay+线段树,90分)

    study from: https://tiger0132.blog.luogu.org/slay-notes P3369 [模板]普通平衡树 #include <cstdio> #inc ...

  6. hdu1890 Robotic Sort (splay+区间翻转单点更新)

    Problem Description Somewhere deep in the Czech Technical University buildings, there are laboratori ...

  7. 2019牛客国庆集训派对day1 K题 双向链表练习题 splay区间翻转

    题目链接: 解法: 先建n颗平衡树,合并的时候将a中最右的结点翻转到根节点,b中最左的结点翻转到根节点,对合并后的根节点进行标记. #include <bits/stdc++.h> usi ...

  8. hdu 1890 Robotic Sort(splay 区间反转+删点)

    题目链接:hdu 1890 Robotic Sort 题意: 给你n个数,每次找到第i小的数的位置,然后输出这个位置,然后将这个位置前面的数翻转一下,然后删除这个数,这样执行n次. 题解: 典型的sp ...

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

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

随机推荐

  1. AtCoder ABC 132F Small Products

    题目链接:https://atcoder.jp/contests/abc132/tasks/abc132_f 题目大意 分析 代码如下

  2. JS与Jquery的事件委托机制

    传送:http://www.ituring.com.cn/article/467 概念: 什么是事件委托:通俗的讲,事件就是onclick,onmouseover,onmouseout,等就是事件,委 ...

  3. Vagrant安装步骤

    Vagrant安装步骤 下载添加box镜像 vagrant box add base 远端的box地址或者本地的box文件名 建立box镜像关联 vagrant box add centos72 va ...

  4. SQL Server Management Studio 的账号密码

    使用“Windows身份验证”方式无法登陆 使用“SQL Server身份验证” 方式无法登陆 解决办法:关闭当前所有服务.通过[Microsoft SQL Server 2008]|[配置工具]|[ ...

  5. Perl 变量

    Perl 变量 变量是存储在内存中的数据,创建一个变量即会在内存上开辟一个空间. 解释器会根据变量的类型来决定其在内存中的存储空间,因此你可以为变量分配不同的数据类型,如整型.浮点型.字符串等. 上一 ...

  6. nginx的配置:目的是使用nginx反向代理后,应用程序获取用户真实ip

    一.了解nginx Nginx是lgor Sysoev为俄罗斯访问量第二的rambler.ru站点设计开发的.从2004年发布至今,凭借开源的力量,已经接近成熟与完善. Nginx功能丰富,可作为HT ...

  7. js面试总结2

    原型和原型链: 题目,知识点,解答: 1.如何准确地判断一个变量是数组类型 2.写一个原型链继承的例子 3.描述new一个对象的过程 4.zepto(或其他框架)源码中如何使用原型链. 知识点 构造函 ...

  8. LUOGU P2860 [USACO06JAN]冗余路径Redundant Paths (双联通,缩点)

    传送门 解题思路 刚开始是找的桥,后来发现这样不对,因为一条链就可以被卡.后来想到应该缩点后找到度数为1 的点然后两两配对. #include<iostream> #include< ...

  9. python实现全局配置和用户配置文件

    一.文件目录格式 二.代码 1.conf.__init__.py import importlib import os from conf import gsettings class Setting ...

  10. HTML5能取代Android和iOS应用程序吗?

    大量新生移动设备的兴起,改变了互联网的未来.在技术的发展上,HTML5会取代App应用吗?或者说能够在多大程度上取代呢?在HTML5规范中,已经加入了相机.磁力罗盘.GPS信息的支持.很多新兴浏览器也 ...