[题解]P3391 文艺平衡树 - Splay解法
给定序列\(1,2,\dots,n\),接下来\(m\)次操作,每次操作给定\(l,r\),你需要翻转\([l,r]\)。
所有操作结束后,请输出这个序列。
我们先从“普通平衡树”这一题出发,思考一下Splay操作的本质。
我们把一个节点Splay到根节点后,中序遍历在自己前面的节点都在左边,中序遍历在自己后面的都在右边。
发现了么?Splay操作和点权是无关的。如果我们将splay(x)
——将\(x\)转移到根节点,修改为splay(x,y)
——将\(x\)转移到\(y\)节点下方(\(y=0\)则表示转移到根节点)。那么splay(x,y)
所做的,就是将\(y\)的子树中,有\(x\)的那棵子树中所有的节点,按与\(x\)中序遍历的先后关系进行调整。
回归这道题。我们先构建一棵中序遍历是\(1,2,\dots,n\)的二叉树(你可以很简单地考虑一条链)。接下来我们思考怎样区间翻转。
我们先考虑一棵树,要想翻转其中序遍历,我们只需要把每个节点的左右子树都翻转一遍即可(不要在意破坏BST的性质,因为这棵树根本就不是BST)。当然这一操作如果实时更新会TLE,所以我们像线段树那样,给根节点打个懒标记,后期需要用到子节点就先下传标记(并交换根节点的左右子树)。
如果我们能把要翻转的\([l,r]\)的所有节点弄到一棵树上去就好了……
现在是Splay大放异彩的时候了。
我们先找到\(l\)在中序遍历中的前驱节点\(L\),\(r\)在中序遍历中的后继节点\(R\)(为了防止找不到,我们设置两个哨兵\(n+1\)和\(0\)一并加入二叉树)。splay(L,0)
,再splay(R,L)
。
我们先将\(L\)转移到根节点,再把\(R\)转移到\(L\)的下面(右子树)。
那么显然,此时\(R\)的左子树的中序遍历位置就都在\(l,r\)之间了。
我们给\(R\)打一个标记即可。
就酱。
受上面操作的启发,我们还可以设计出以下操作(一般这些操作都需要哨兵\(n+1\)和\(0\)的辅助,同时splay
需要接受\(2\)个参数):
- \([l,r]\)区间删除:和上边一样,
splay(l,0)
后splay(r,l)
,断开\(r\)与其左子树的连接。 - 合并以\(x,y\)为根的\(2\)颗Splay树(前提是两树都不为空,且\(x\)中的最大值\(<y\)中的最小值):把\(x\)中的最大值Splay到根节点,将其右子节点设为\(y\)并更新自己的信息即可。
- 区间赋值、区间加……一般线段树能维护的这个也能维护,只是可能与一些平衡树特有的功能冲突。
实现细节:
- 别忘了
find(k)
(找中序遍历第\(k\)个)和ltr(u)
(中序遍历输出结果)要调用pushdown
。
此题的代码
#include<bits/stdc++.h>
#define int long long
#define N 100010
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
#define ch(x,y) tr[x].ch[y]
#define fa(x) tr[x].fa
#define v(x) tr[x].v
#define siz(x) tr[x].siz
#define tag(x) tr[x].tag
#define MAX LLONG_MAX
#define MIN LLONG_MIN
using namespace std;
struct tree{
int ch[2],siz,fa,v;
bool tag;
}tr[N];
int n,m,cnt,root;
int newnode(int v){v(++cnt)=v,siz(cnt)=1;return cnt;}
void update(int u){siz(u)=siz(lc(u))+siz(rc(u))+1;}
bool get(int u){return u==rc(fa(u));}
void rot(int x){//通过旋转把x提升1层
int y=fa(x),z=fa(y);//保证y!=0
bool dir=get(x),tdir=get(y);
if(ch(x,!dir)) fa(ch(x,!dir))=y;
ch(y,dir)=ch(x,!dir);
ch(x,!dir)=y;
fa(y)=x,fa(x)=z;
if(z) ch(z,tdir)=x;
update(y),update(x);
}
void splay(int x,int y){//核心操作
for(int f;(f=fa(x))!=y;rot(x))
if(fa(f)!=y) rot(get(f)==get(x)?f:x);
if(y==0) root=x;
}
void pushdown(int x){//下放标记
if(tag(x)){
swap(lc(x),rc(x));
tag(lc(x))^=1;
tag(rc(x))^=1;
tag(x)=0;
}
}
void ins(int v){//插入
int u=root,f=0;
while(u) f=u,u=ch(u,v>v(u));//>在右,<=在左
u=newnode(v),fa(u)=f;
if(f) ch(f,v>v(f))=u;
splay(u,0);
}
int find(int num){//中序遍历第num个是多少
int u=root;
while(u){
pushdown(u);
if(num==siz(lc(u))+1) return u;
if(num<=siz(lc(u))) u=lc(u);
else num-=siz(lc(u))+1,u=rc(u);
}
return -1;
}
void ltr(int u){//中序遍历,输出
if(!u) return;
pushdown(u);
ltr(lc(u));
if(v(u)!=MIN&&v(u)!=MAX) cout<<v(u)<<" ";
ltr(rc(u));
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m;
ins(MIN),ins(MAX);
for(int i=1;i<=n;i++) ins(i);
while(m--){
int l,r;
cin>>l>>r;
l=find(l),r=find(r+2);
//因为有极小值,所以查询的名次都要+1
splay(l,0);
splay(r,l);
if(lc(r)) tag(lc(r))^=1;
}
ltr(root);
return 0;
}
注意到,find
函数是没有Splay操作的。其实加上也可以,只不过find
后面已经有\(2\)次Splay操作了。加不加其实都差不多。
实际上,初始化完全不需要调用ins
,直接建成一条链即可,这样显然不会影响后面操作的复杂度(因为就算调用ins
也可能形成链嘛),而且把初始化的复杂度减少了一个\(\log\)。
[题解]P3391 文艺平衡树 - Splay解法的更多相关文章
- [洛谷P3391] 文艺平衡树 (Splay模板)
初识splay 学splay有一段时间了,一直没写...... 本题是splay模板题,维护一个1~n的序列,支持区间翻转(比如1 2 3 4 5 6变成1 2 3 6 5 4),最后输出结果序列. ...
- 【阶梯报告】洛谷P3391【模板】文艺平衡树 splay
[阶梯报告]洛谷P3391[模板]文艺平衡树 splay 题目链接在这里[链接](https://www.luogu.org/problemnew/show/P3391)最近在学习splay,终于做对 ...
- luoguP3391[模板]文艺平衡树(Splay) 题解
链接一下题目:luoguP3391[模板]文艺平衡树(Splay) 平衡树解析 这里的Splay维护的显然不再是权值排序 现在按照的是序列中的编号排序(不过在这道题目里面就是权值诶...) 那么,继续 ...
- [luogu P3391] 文艺平衡树
[luogu P3391] 文艺平衡树 题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区 ...
- P3391 文艺平衡树(Splay)
题目背景 这是一道经典的Splay模板题--文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1, ...
- Tyvj P1729 文艺平衡树 Splay
题目: http://tyvj.cn/p/1729 P1729 文艺平衡树 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 此为平衡树系列第二道:文艺平衡树 ...
- BZOJ3223 文艺平衡树(splay)
题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1, ...
- BZOJ3223: Tyvj 1729 文艺平衡树 [splay]
3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3595 Solved: 2029[Submit][Sta ...
- BZOJ 3223: Tyvj 1729 文艺平衡树(splay)
速度居然进前十了...第八... splay, 区间翻转,用一个类似线段树的lazy标记表示是否翻转 ------------------------------------------------- ...
- BZOJ3223/洛谷P3391 - 文艺平衡树
BZOJ链接 洛谷链接 题意 模板题啦~2 代码 //文艺平衡树 #include <cstdio> #include <algorithm> using namespace ...
随机推荐
- mac ssh 总是自动断开
创建一个ssh配置文件: vi ~/.ssh/config 写入以下内容: Host * ServerAliveInterval 120 TCPKeepAlive no
- 【中英】【吴恩达课后测验】Course 5 -序列模型 - 第二周测验 - 自然语言处理与词嵌入
[中英][吴恩达课后测验]Course 5 -序列模型 - 第二周测验 - 自然语言处理与词嵌入 上一篇:[课程5 - 第一周编程作业]※※※※※ [回到目录]※※※※※下一篇:[课程5 -第二周编程 ...
- Springboot笔记<5>静态资源访问
静态资源访问 静态资源目录 请求进来,先去找Controller看能不能处理.不能处理的所有请求又都交给静态资源处理器.静态资源也找不到则响应404页面.如果静态目录中存在a.png,访问localh ...
- 冲刺 CSP 联训模拟2
题面 温馨提示 代码中的变量名可能与题意.题解不同. 代码不删缺省源,可以直接拿来对拍. T1 挤压 Solution 众所周知,异或是一种按位运算,不好进行十进制的数间合并.我们考虑将每个数拆分为二 ...
- TypeScript结构化类型初探
啥是鸭子类型? 作为一个前端程序员,想必大家都知道javascript是一个弱类型语言,如果需要类型的支持,那就需要借助typescript来实现,但是大家可曾听过这样一个说法? javascript ...
- Windows 系统 局域网文件夹共享无法访问的终极解决方法
先介绍 Win10 无法访问其他电脑的解决方法 首先,Win10 能成功访问共享文件夹,必须有安装 SMB1 协议,否则会提示找不到网络名称的提示. 方法很简单,点击 微软小娜 Cortana 输入 ...
- Xamarin.Andorid 监听 EditText 回车事件
EditText ET_Billcode.EditorAction += ET_Billcode_EditorAction; //执行方法 private void ET_Billcode_Edito ...
- FastGithub 使用遇到问题
火狐浏览器 https://blog.csdn.net/weixin_33847182/article/details/86129219 因 HTTP 严格传输安全(HSTS)机制无法打开网页 1.打 ...
- .NET 8性能优化全攻略:让你的应用飞起来!
大家好!我是.NET修仙日记的掌门人,今天我们来聊聊.NET 8的性能优化技巧.随着.NET 8的发布,微软带来了更多性能改进的可能性.无论你是开发Web应用.微服务还是桌面程序,这些优化技巧都能让你 ...
- 树莓派4b安装openwrt做副路由
下载镜像https://github.com/SuLingGG/OpenWrt-Rpi 刻录镜像 balenaEtcher刻录即可,其他软件也行 修改opwrt路由器ip 刻录完系统到tf卡,将其插入 ...