「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$个选中区间包含,且选中区间长度的极差最小. 区间题死脑筋晚期:把区间按左端点排序,然后右端点用个优先队列来弹,然后需要维护下 ...
随机推荐
- 朱晔和你聊Spring系列S1E6:容易犯错的Spring AOP
阅读PDF版本 标题有点标题党了,这里说的容易犯错不是Spring AOP的错,是指使用的时候容易犯错.本文会以一些例子来展开讨论AOP的使用以及使用过程中容易出错的点. 几句话说清楚AOP 有关必要 ...
- python第三章:循环语句--小白博客
Python条件语句 Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块. 可以通过下图来简单了解条件语句的执行过程: Python程序语言指定任何非0和非 ...
- 十五、bootstrap-select的使用方法
参考来源https://www.cnblogs.com/nianyifenzhizuo/p/8119462.html 需要的css和js <link rel="stylesheet&q ...
- 美团2016秋招笔试B
1.下述解决死锁的方法中,属于死锁预防策略的是? 资源有序分配法 银行家算法:避免死锁 资源有序分配法:预防死锁 资源分配图化简法:检测死锁 撤销进程法:解决死锁 2. 什么是死锁? 如果一个进 ...
- Python-生成器_36
#生成器函数 def generator(): print(1) return 'a' ret = generator() print(ret) #只要含有yield关键字的函数都是生成器函数 # y ...
- 小程序wxRequest封装
//const host = 'http://114.215.00.00:8005';// 测试地址 const host = 'https://xx.xxxxxxxx.net'; // 正式地址 c ...
- rabbitmq集群运维一点总结
说明:以下操作都以三节点集群为例,机器名标记为机器A.机器B.机器C,如果为双节点忽略机器C,如果为各多节点则与机器C操作相同 一.rabbitmq集群必要条件 1.1.绑定实体ip,即ip a所能查 ...
- [已解决]关于python无法显示中文的问题:SyntaxError: Non-ASCII character '\xe4' in file test.py on line 3, but no encoding declared。
想在python代码中输出汉字.但是老是出现SyntaxError: Non-ASCII character '\xe4' in file test.py on line , but no encod ...
- javascript重定向页面并用post方法传递消息
javascript中重定向页面得方法很多,同时能传递消息的也不少:但可用post方法传递的我只找到两种: 第一种方法:用document.write在 JavaScript函数中,用document ...
- html问题汇总
1.textarea换行 textarea中无法使用<br/>换行,需要使用\n 2.textarea无法提交 我们知道表单中的元素需要设置name属性才能够提交,但是如果设置了disab ...