「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$个选中区间包含,且选中区间长度的极差最小. 区间题死脑筋晚期:把区间按左端点排序,然后右端点用个优先队列来弹,然后需要维护下 ...
随机推荐
- 纯手写AJAX
function ajax(){ //http相应对象 var xmlhttp; //判断浏览器 if(window.XMLHttpRequest){ xmlhttp = new XMLHttpReq ...
- 平均精度均值(mAP)——目标检测模型性能统计量
在机器学习领域,对于大多数常见问题,通常会有多个模型可供选择.当然,每个模型会有自己的特性,并会受到不同因素的影响而表现不同. 每个模型的好坏是通过评价它在某个数据集上的性能来判断的,这个数据集通常被 ...
- C#.NET 大型通用信息化系统集成快速开发平台 4.1 版本 - 严格的用户账户审核功能
整个集团有几万个用户,一个个用户添加是不现实的,只有每个公司的系统管理员添加.或者用户申请帐户,然后有相应的管理员审核,才会更准确一些. 每个公司.分公司.部门的账户情况只有所在公司的管理员是最清楚的 ...
- vuex状态管理工具
父子组件之间的通信 props传递 父 向子单向传递:且每次 父组件更新时 子组件的props会跟着更新: 如果需要 子组件把数据传递给父组件,就需要在子组件上绑定自定事件 在子组件使用this ...
- 牛客练习赛35 C.函数的魔法
链接 [https://ac.nowcoder.com/acm/contest/32] 题意 题目描述 一位客人来到了此花亭,给了女服务员柚一个数学问题:我们有两个函数,F(X)函数可以让X变成(XX ...
- Day4 Python基础之数据类型(三)
计算机中,一切皆为对象 世界万物,皆为对象,一切对象皆可分类 ------------------------------------我是分割线---------------------------- ...
- CRM系统(第二部分)
阅读目录 一.讲师与学生简介 二. 初始化 ,studyrecord, 三.初始化 course_record:批量生成学习记录 四. 考勤 url跳转 五.录入成绩 六.highcharts表 ...
- 【学习总结】GirlsInAI ML-diary day-4:变量/Variable
[学习总结]GirlsInAI ML-diary 总 原博github链接-day4 变量/Variable 变量是计算机编程中一个很基础的概念,在计算机程序中,variables are reser ...
- Nginx Configuring HTTPS servers
Configuring HTTPS servershttp://nginx.org/en/docs/http/configuring_https_servers.html Configuring HT ...
- 单个源文件下CmakeList.txt
单个源文件下CmakeList.txt 1. main.c代码 & CmakeLists.txt 文件内容 在任意自己选定的目录下(t1/)编写main.c 与 CmakeLists.txt ...