BZOJ 3223 文艺平衡树 [codevs3303翻转区间]
AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=3223
通道2:http://codevs.cn/problem/3303/
题目分析:
我们先想想数组怎么操作。
每次O(1)找到区间位置,然后旋转,大概是O(r-l+1)的。
这样太慢了,每次我都将操作进行到底了。可是翻转标记有时候其实不需要每个都去办,因为它们之间还可以互相抵消。
能不能用线段树呢?
找到区间log(n),标记一下O(1),可以过?
不行!这个翻转显得与往常处理的线段树题目不一样,因为之前线段树是静态的数轴,但是这个数轴会翻转!
直接根据下标找到的只是数值,不是现在它在数组中的位置。每次怎么找到新的区间位置?
每次去找的时候,将标记下传然后翻转线段树[交换左右节点]好像就行了...
线段树真的可以翻转么?我每个节点的信息岂不是非常混乱!下次再想找到这个区间就不容易了,因为我线段树的数值不满足当初的性质了。
通过刚才的分析,发现这题中的数值其实没有多大作用,只是它特殊的一个标记而已,重要的还是它在序列中的位置,以及如何处理好翻转与查询的关系。
翻转是不能彻底进行的,要用标记且标记不能下传到底,但是在这样的条件下要有一种快速的算法,让我们找到我们需要的区间。
标记让人想到树结构,快速找区间又想到二叉树,于是不妨维护一颗搜索二叉树,其中以这个元素在序列中的位置作为排序的标准。
在每次寻找左右端点时,即使有一些节点的标记还未下传,但是只要这个节点下的子树的元素个数小于当前我的查询值,我就可以跳过它。
反之,就将它的标记下传,并让它的左右子树交换位置,依次往下,直到找到自己需要的那个节点上。
节点到区间的转变怎么实现呢?这边需要Splay()操作了。
首先将l-1转到根节点上来,然后将r+1转到l-1的右节点上。

这样[l,r]就很清晰而且全部存在于一棵子树下了。
有没有觉得这个算法的神奇之处啊?或是说Splay()也可以这样用,标记也可以这样用...Orz
当然这题还有几个地方需要注意,一是当l=1时,需要将排行第0的节点转上来(如果是n的话,你多虚拟一个n+1的节点是可以的)...不可实现啊,
【其实你也可以特判掉,在这种情况下只找r+1翻到顶上(对于n也可以这样特判)】,不过...笔者表示膜别人思路将所有节点的下标前移了一个,所以代码中表现的是将l和r+2进行操作。
二是刚开始建树的话,不仅要注意建到n+2【如果采用特判则是n+1】,也要尽量让它初始就平衡一点哦...
#include<cstdio>
#include<cstring>
#include<algorithm> using namespace std; inline int in(){
int x=;char ch=getchar();
while(ch>'' || ch<'') ch=getchar();
while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
return x;
} const int maxn=; int n,m,rt; struct Node{
int sz,f;
int l,r;
bool mk; void trans(){swap(l,r);}
}s[maxn]; inline void updata(int x){
s[x].sz=s[s[x].l].sz+s[s[x].r].sz+;
} int build(int l,int r){
if(l>r) return ;
int mid=(l+r)>>;
s[mid].l=build(l,mid-);
s[mid].r=build(mid+,r);
updata(mid);
s[s[mid].l].f=mid,s[s[mid].r].f=mid;
return mid;
} inline void push_down(int x){
if(s[x].mk){
s[x].trans();
s[s[x].l].mk^=;
s[s[x].r].mk^=;
s[x].mk=;
}
} void zig(int x){
int y=s[x].f; s[x].f=s[y].f;
if(s[y].f){
if(y==s[s[y].f].l) s[s[y].f].l=x;
else s[s[y].f].r=x;
} s[y].l=s[x].r;
if(s[x].r)
s[s[x].r].f=y; s[y].f=x,s[x].r=y; updata(y);updata(x);
} void zag(int x){
int y=s[x].f; s[x].f=s[y].f;
if(s[y].f){
if(y==s[s[y].f].l) s[s[y].f].l=x;
else s[s[y].f].r=x;
} s[y].r=s[x].l;
if(s[x].l)
s[s[x].l].f=y; s[x].l=y,s[y].f=x; updata(y);updata(x);
} void Splay(int x,int gf){
int y;
while(s[x].f!=gf){
y=s[x].f;
if(s[y].f==gf){
if(x==(s[y].l)) zig(x);
else zag(x);
}
else{
int z=s[y].f;
if(y==s[z].l){
if(x==s[y].l) zig(y),zig(x);
else zag(x),zig(x);
}
else{
if(x==s[y].r) zag(y),zag(x);
else zig(x),zag(x);
}
}
}
if(!gf) rt=x;
} int find(int k){
int p=rt;
if(k>s[p].sz) return ;
while(p){
push_down(p);//每访问到一个节点,就将它的标记下传,从这个点到根的路上就都传完了
if(k<=s[s[p].l].sz) p=s[p].l;
else{
k-=s[s[p].l].sz;
if(k==) return p;
k--,p=s[p].r;
}
}
} void rev(int l,int r){
int a=find(l),b=find(r+);//这里是找到这两个需要翻转的节点的位置
Splay(a,);Splay(b,a);
s[s[b].l].mk^=;
} void print(int x){
if(!x) return;
push_down(x);
print(s[x].l);
if(x> && x<=n+) //有几个节点是根据需要虚拟出的,它们不能输出
printf("%d ",x-);
print(s[x].r);
} int main(){
#ifndef ONLINE_JUDGE
freopen("3223.in","r",stdin);
freopen("3223.out","w",stdout);
#endif int l,r; n=in();m=in();
rt=build(,n+);
while(m--)
l=in(),r=in(),rev(l,r);
print(rt);
return ;
}
BZOJ 3223 文艺平衡树 [codevs3303翻转区间]的更多相关文章
- bzoj3223 文艺平衡树 codevs3303 翻转区间
splay模版题吧 只有区间翻转 至于为什么要把须翻转区间旋到根 因为查找一个区间可以先找出他左端点左边第一个点和右端点x右边第一个点y 然后将x旋到根节点 y旋到x的右儿子 这样x的右边的点就是所有 ...
- [题解]bzoj 3223 文艺平衡树
3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3884 Solved: 2235[Submit][Sta ...
- bzoj 3223 文艺平衡树 - Splay
3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3884 Solved: 2235[Submit][Sta ...
- BZOJ 3223 文艺平衡树
Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 ...
- bzoj 3223 文艺平衡树 splay 区间翻转
Tyvj 1728 普通平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 17715 Solved: 7769[Submit][Status][ ...
- bzoj 3223 文艺平衡树 Splay 打标志
是NOI2003Editor的一个子任务 #include <cstdio> #include <vector> #define maxn 100010 using names ...
- BZOJ 3223: Tyvj 1729 文艺平衡树-Splay树(区间翻转)模板题
3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 6881 Solved: 4213[Submit][Sta ...
- [BZOJ 3223 & Tyvj 1729]文艺平衡树 & [CodeVS 3243]区间翻转
题目不说了,就是区间翻转 传送门:BZOJ 3223 和 CodeVS 3243 第一道题中是1~n的区间翻转,而第二道题对于每个1~n还有一个附加值 实际上两道题的思路是一样的,第二题把值对应到位置 ...
- [bzoj3224]普通平衡树/3223文艺平衡树
这是一道很普通的题.. 最近花了很多时间来想要去干什么,感觉自己还是太拿衣服 做这道题是因为偶尔看到了lavender的blog和她的bzoj早期AC记录,就被题目深深地吸引到了,原因有二: 自己sp ...
随机推荐
- [leetcode]_Remove Nth Node From End of List
题目:移除linked-list从尾到头的第N个元素 自我思路:因为题目给出的N是从链表尾开始计算的,单链表不存在从子指向父亲的反向指针,因此我先计算链表的整个长度len,然后用len - N来表示正 ...
- Java ZK image 處理
自己上 ZK 官方論壇提問.別人的回答.自己解決問題後上傳的問題紀錄: http://forum.zkoss.org/question/101152/how-do-i-binding-image-fi ...
- 分享:php 上传图片的代码
转自:http://www.jbxue.com/article/6379.html php 上传图片的代码,很简单,实现了基本的文件类型.文件大小的检测,并实现了基本的水印与缩略功能,比较适合初学的朋 ...
- PopupWindow的简单使用
测试代码: package com.zzw.testpopuwindows; import android.app.Activity; import android.graphics.Color; i ...
- C#读取和写入配置文件
使用.Net2.0中的ConfigurationManager可以方便的实现对配置app.config的读取和写入. ConfigurationManager默认没有自动载入项目,使用前必须手动添加, ...
- 2.python的变量与赋值
首先,为何要使用变量这里就不再多说了,我这里就介绍一下变量的命令规则和变量赋值的内存行为. 1.变量的命名规则 变量其实通过一个标记调用内存中的值,而变量名就是这个标记的名称,但是万一这个标记已经被提 ...
- 009-python基础-数据类型-列表和元组
一.列表 在python中叫"列表",其他语言中成为"数组" 元素中可以存储字符串.数字甚至变量. 元素索引顺序从0开始. 例如 name_list[0] 就是 ...
- 【笔记】WPF实现ViewPager引导界面效果及问题汇总
最近在开发项目的首次使用引导界面时,遇到了问题,引导界面类似于安卓手机ViewPager那样的效果,希望通过左右滑动手指来实现切换不同页面,其间伴随动画. 实现思路: 1.界面布局:新建一个UserC ...
- throw和throws
uncheckException的处理 class User{ private int age; public void setAge(int age){ if(age < 0){ //生成异常 ...
- [转]关于VC预定义常量_WIN32,WIN32,_WIN64
VC2012 下写 Windows 程序时,有时需要判断编译环境.在之前的文章<判断程序是否运行在 Windows x64 系统下.>里说过如何在运行期间判断系统环境,但在编译时如何判 ...