「Splay」区间翻转
传送门:>Here<
解法分析
用splay来维护这个序列。
一直没有搞明白的是,这里的splay的节点究竟维护的是什么?是权值吗?肯定不是,因为区间是会翻转的,如果维护权值的话很快平衡树就不再满足性质。
然而从头到尾,唯一始终统一的就是位置——始终是1~n. 因此考虑用节点来维护位置。
这样在维护splay的时候,翻转一段区间就相当于修改了这一段区间的位置,使原来小的现在大了,原来大的现在小了。放在树上形象的看,就是原来作为父节点的左儿子的统统称为了右儿子。反之亦然。因此只要找出连续的那一段区间,并且翻转左右子树即可。注意为了减少操作次数,可以打懒标记。
如何找出连续的那一段目标区间?由于要翻转的是区间$[l, r]$,我们可以找到位置(注意,这里的位置也就是平衡树维护的权值的排名了,因此位置i也就是排名第i的)l-1和r+1的点,分别旋转到根节点和根节点的右子树上。这样,根节点的右子节点的左子树就是这段区间了。(想一想,为什么)。特殊的,当边界到达1或n时会溢出,因此我们加入哨兵节点-1和n+1。这样位置i就是排名i+1了。
值得注意的是何时下传懒标记——根据懒标记的优良传统,不用就不翻转。那唯一要用的时候就是询问位置的时候了,因此只要find的时候下传就可以了。另外翻转两次相当于不翻转,所以用xor1会很方便。
/*By QiXingzhi*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define r read()
#define Max(a,b) (((a)>(b)) ? (a) : (b))
#define Min(a,b) (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int MAXN = ;
const int INF = ;
inline int read(){
int x = ; int w = ; register int c = getchar();
while(c ^ '-' && (c < '' || c > '')) c = getchar();
if(c == '-') w = -, c = getchar();
while(c >= '' && c <= '') x = (x << ) +(x << ) + c - '', c = getchar();
return x * w;
}
int n,m,L,R;
struct Splay{
int ch[MAXN][],size[MAXN],val[MAXN],fa[MAXN],tag[MAXN],num_node,root;
inline bool Son(int f, int x){
return ch[f][]==x;
}
inline void Update(int x){
size[x] = size[ch[x][]] + size[ch[x][]] + ;
}
inline void Rotate(int x){
int f = fa[x], gf = fa[f];
bool p = Son(f, x), q = !p;
ch[f][p] = ch[x][q];
fa[ch[x][q]] = f;
ch[x][q] = f;
fa[f] = x;
fa[x] = gf;
if(gf != ){
ch[gf][Son(gf,f)] = x;
}else{
root = x;
}
Update(x), Update(f);
}
inline void splay(int x, int target){
while(fa[x] != target){
int f = fa[x], gf = fa[f];
if(gf == target){
Rotate(x);
return;
}
if(Son(gf,f) == Son(f,x)){
Rotate(f), Rotate(x);
}
else{
Rotate(x), Rotate(x);
}
}
}
inline void Insert(int v){
if(root == ){
++num_node;
root = num_node;
size[num_node] = ;
val[num_node] = v;
return;
}
for(int x = root; x != ; x = ch[x][v >= val[x]]){
bool b = v >= val[x];
if(ch[x][b] == ){
++num_node;
ch[x][b] = num_node;
size[num_node] = ;
val[num_node] = v;
fa[ch[x][b]] = x;
splay(ch[x][b], );
}
}
}
inline void Pushdown(int x){
if(x == ) return;
if(!tag[x]) return;
int tmp = ch[x][];
ch[x][] = ch[x][];
ch[x][] = tmp;
tag[x] = ;
tag[ch[x][]] ^= ;
tag[ch[x][]] ^= ;
}
inline int Find(int _k){
int x = root;
for(; x != ;){
Pushdown(x);
if(size[ch[x][]] + == _k){
return x;
}
if(size[ch[x][]] >= _k){
x = ch[x][];
}
else{
_k -= size[ch[x][]] + ;
x = ch[x][];
}
}
}
inline void Reverse(int L, int R){
if(L >= R) return;
splay(Find(L), );
splay(Find(R), root);
tag[ch[ch[root][]][]] ^= ;
}
inline void DEBUG(){
for(int i = ; i <= n; ++i){
printf("%d-->%d lson:%d rson:%d\n",i,val[i],ch[i][],ch[i][]);
}
}
}qxz;
int main(){
// freopen(".in","r",stdin);
n=r, m=r;
qxz.Insert(-);
qxz.Insert(n+);
for(int i = ; i <= n; ++i){
qxz.Insert(i);
}
// qxz.DEBUG();
for(int i = ; i <= m; ++i){
L=r, R=r;
qxz.Reverse(L,R+);
}
for(int i = ; i <= n; ++i){
printf("%d ", qxz.val[qxz.Find(i+)]);
}
return ;
}
「Splay」区间翻转的更多相关文章
- 「NOI2016」区间 解题报告
「NOI2016」区间 最近思维好僵硬啊... 一上来就觉得先把区间拆成两个端点进行差分,然后扫描位置序列,在每个位置维护答案,用数据结构维护当前位置的区间序列,但是不会维护. 于是想研究性质,想到为 ...
- 「SDOI2005」区间
「SDOI2005」区间 传送门 记录每一个位置作为左端点和右端点的出现次数,然后直接考虑差分即可. 参考代码: #include <cstdio> #define rg register ...
- bzoj3223 Tyvj 1729 文艺平衡树(Splay Tree+区间翻转)
3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2202 Solved: 1226[Submit][Sta ...
- BZOJ 3223: Tyvj 1729 文艺平衡树-Splay树(区间翻转)模板题
3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 6881 Solved: 4213[Submit][Sta ...
- 【模板】文艺平衡树(Splay) 区间翻转 BZOJ 3223
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 N,M<= ...
- splay(1区间翻转区间最值与区间修改)
bzoj1251权限题 题目点这里,你懂得 直接上板子,这个要好好体会 操作是最经典的. #include <algorithm> #include <iostream> #i ...
- hdu1890 splay维护区间翻转
这题的建模有点不太一样,是按结点横坐标赋予键值的 同时每次rotate和splay时都要注意下往上往下更新 /* 先建立好splay tree,将结点按num/输入顺序排序,遍历时每次将当前结点提到根 ...
- 「Splay」普通平衡树模板
口诀: $rotate$:先上再下,最后自己 $splay$:祖父未到旋两次,三点一线旋父亲,三点折线旋自己. $delete$:没有儿子就删光.单个儿子删自己.两个儿子找前驱. 易错点: $rota ...
- LOJ#2086. 「NOI2016」区间
$n \leq 500000$个区间,从中挑出一些,使得至少有一个点被$m$个选中区间包含,且选中区间长度的极差最小. 区间题死脑筋晚期:把区间按左端点排序,然后右端点用个优先队列来弹,然后需要维护下 ...
随机推荐
- 关于eclipse tomcat 无法启动(8080,8005,8009端口被占用)的解决方法,附 eclipse tomcat 与 tomcat 并存方式
eclipse 在编译运行时 新建的tomcat连接始终为stopped状态,描述为8080,8005,8009端口被占用. 这是因为在装完tomcat后,tomcat服务已启动,而eclipse仅仅 ...
- Windows下的两个缺陷
记事本缺陷: 标题:新建记事本中仅输入“联通”,保存关闭后再打开,显示为乱码 详细描述: 环境说明:操作系统ALL 重现步骤: 1.新建一个记事本,在其中仅输入“联通”两个字 2.再将该记事本关闭保存 ...
- Nginx监控运维
Nginx是一个开源.免费.高性能的HTTP和反向代理服务器,也可以用于IMAP/POP3代理服务器.充分利用Nginx的特性,可以有效解决流量高并发请求.cc攻击等问题. 本文探讨了电商场景下Ngi ...
- HDU - 1255 扫描线+离散化进阶
这道题最开始我以为和HDU - 1542 那道题一样,只需要把cover次数改成2次即可,但是后面仔细一想,我们需要求的是覆盖次数大于等于2次的,这样的话,我们需要维护两个长度,HDU-1542 由于 ...
- 漫谈数组去重复方法(亮点是ES6的新API)
方法1: 利用遍历的思想来进行. <!DOCTYPE html><html lang="en"><head> <meta charset= ...
- echarts各个配置项详细说明总结
https://blog.csdn.net/sinat_34492035/article/details/70258557 https://blog.csdn.net/qq_34908167/arti ...
- ElasticSearch(简称ES)
Windows下安装ElasticSearch ElasticSearch(简称ES)是一个基于Lucene的分布式全文搜索服务器,和SQL Server的全文索引(Fulltext Index) ...
- Java面试题详解四:==和equals的去别
一,功能 1.对于== 作用于基本数据类型的变量,比较的存储的值是否相等, 作用于引用类型的变量,比较的是其所指向的对象的地址是否相同(即是否是同一个对象) 2.对于equals Object的equ ...
- 手机移动端input date placehoder不显示
要解决这个问题,我们可以伪造一个placehoder,通过css跟js来解决这个问题. 为什么要用js的原因是因为当你选择了时间之后,placehoder的文字没有清除掉,所以我们就需要把这个伪造的p ...
- scroll滚动条样式修改
一般我们有两种情况会出现滚动条,一种是overflow,一种是使用scroll. 当我们需要改变这个滚动条样式的时候,我们需要做以下的修改: html: <div id="style- ...