[HEOI2016/TJOI2016] 排序 解题报告(二分答案/线段树分裂合并+set)
题目链接:
https://www.luogu.org/problemnew/show/P2824
题目描述:
在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他。这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序。最后询问第q位置上的数字。
题解:
做法一:二分答案
然而我并没有写,口胡一下就是二分最终的那个数字,假设我们二分的值是$val$,那么在序列中大于等于$val$我们看成1,小于$val$的我们看成0,这样就是一个01序列了。每次操作我们就线段树维护0的个数和1的个数然后区间赋值就是了。最终我们看一下第q个位置是0还是1,如果是1就把$val$调大,否则调小。
做法二:线段树分裂合并+set
主要讲讲这个吧
考虑我们一开始对每个元素建一颗动态开点权值线段树,因为是1到n的排列我们都不需要离散化(其实本身是动态开点好像也没有很大的影响)。这样我们就得到了一个个由一个元素构成的区间,在之后的操作中然后我们用set维护一下每个区间的左端点,右端点,这个区间线段树的根节点和这个区间是单调增的还是单调减的。
不管是操作1还是操作2,我们就是把[l,r]的所有元素合并到一棵线段树,唯一麻烦的就是端点l,r可能是在当前的某个区间里面,这样的话我们还需要把这个区间给分裂,再把我们需要的那部分拿出来合并
代码实现是这样的,注意我们还要修改一下set
set<Dat>::iterator it=s.lower_bound((Dat){,l,,});
if ((*it).l!=l)
{
Dat t=*it;s.erase(it);int tn=;
if (t.type==)
{
Seg::split(,n,t.nd,tn,l-t.l);
s.insert((Dat){t.l,l-,tn,});
s.insert((Dat){l,t.r,t.nd,});
//printf("qq%d %d\n",t.l,l-1);
//printf("qq%d %d\n",l,t.r);
}
else
{
Seg::split(,n,t.nd,tn,t.r-l+);
s.insert((Dat){t.l,l-,t.nd,});
s.insert((Dat){l,t.r,tn,});
}
}
it=s.lower_bound((Dat){,r,,});
if ((*it).r!=r)
{
Dat t=*it;s.erase(it);int tn=;
if (t.type==)
{
Seg::split(,n,t.nd,tn,r-t.l+);
//printf("qq%d\n",tn);
s.insert((Dat){t.l,r,tn,});
s.insert((Dat){r+,t.r,t.nd,});
}
else
{
Seg::split(,n,t.nd,tn,t.r-r);
s.insert((Dat){t.l,r,t.nd,});
s.insert((Dat){r+,t.r,tn,});
}
}
it就是我们当前要分裂的区间,(*it).nd就是这个区间的根节点,l,r是我们这次操作的区间。对于Dat这个结构体我们采取右端点优先,左端点次一级的排序。
由于升降序不同,我们要从要分裂的区间的线段树取出来的部分也不同,可能是后面的,也可能是前面的,这就需要分类讨论一下了(不要怕麻烦,懒得话就不要学这种做法)
还学到了一个回收空间内存的做法,具体时间就是用一个栈保存一下可以使用的结点,每次要用结点的时候让一个元素出栈即可,可见getnode函数
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<set>
using namespace std; int n,m;
namespace Seg
{
const int N=6e5+;
int lx[N],rx[N],siz[N];
int st[N+];int top;
void init() {for (int i=N-;i>=;i--) st[++top]=i;}
int getnode() {int t=st[top--];lx[t]=rx[t]=siz[t]=;return t;}
void delnode(int x) {st[++top]=x;}
#define mid ((l+r)>>1)
void update(int &o,int l,int r,int x)
{
if (!o) o=getnode();
if (l==r) {siz[o]++;return;}
if (x<=mid) update(lx[o],l,mid,x);
else update(rx[o],mid+,r,x);
siz[o]=siz[lx[o]]+siz[rx[o]];
}
void split(int l,int r,int o,int &x,int k)
{
if (!k) return;
if (!x) x=getnode();
if (l==r) {siz[o]-=k;siz[x]+=k;return;}
int t=siz[lx[o]];
if (t>k) split(l,mid,lx[o],lx[x],k);
if (t==k) lx[x]=lx[o],lx[o]=;
if (t<k)
{
lx[x]=lx[o];lx[o]=;
split(mid+,r,rx[o],rx[x],k-t);
}
siz[o]=siz[lx[o]]+siz[rx[o]];
siz[x]=siz[lx[x]]+siz[rx[x]];
}
int merge(int x,int y)
{
if (!x||!y) return x|y;
lx[x]=merge(lx[x],lx[y]);
rx[x]=merge(rx[x],rx[y]);
siz[x]+=siz[y];
delnode(y);
return x;
}
int kth(int l,int r,int o,int k)
{
if (l==r) return l;
if (siz[lx[o]]>=k) return kth(l,mid,lx[o],k);
else return kth(mid+,r,rx[o],k-siz[lx[o]]);
}
}
struct Dat
{
int l,r,nd,type;
};
bool operator <(const Dat &a,const Dat &b)
{
return a.r<b.r||(a.r==b.r&&a.l<b.l);
}
set <Dat> s;
inline int read()
{
char ch=getchar();
int s=,f=;
while (ch<''||ch>'') {if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<='') {s=(s<<)+(s<<)+ch-'';ch=getchar();}
return s*f;
}
int split_node(int l,int r)
{
set<Dat>::iterator it=s.lower_bound((Dat){,l,,});
if ((*it).l!=l)
{
Dat t=*it;s.erase(it);int tn=;
if (t.type==)
{
Seg::split(,n,t.nd,tn,l-t.l);
s.insert((Dat){t.l,l-,tn,});
s.insert((Dat){l,t.r,t.nd,});
//printf("qq%d %d\n",t.l,l-1);
//printf("qq%d %d\n",l,t.r);
}
else
{
Seg::split(,n,t.nd,tn,t.r-l+);
s.insert((Dat){t.l,l-,t.nd,});
s.insert((Dat){l,t.r,tn,});
}
}
it=s.lower_bound((Dat){,r,,});
if ((*it).r!=r)
{
Dat t=*it;s.erase(it);int tn=;
if (t.type==)
{
Seg::split(,n,t.nd,tn,r-t.l+);
//printf("qq%d\n",tn);
s.insert((Dat){t.l,r,tn,});
s.insert((Dat){r+,t.r,t.nd,});
}
else
{
Seg::split(,n,t.nd,tn,t.r-r);
s.insert((Dat){t.l,r,t.nd,});
s.insert((Dat){r+,t.r,tn,});
}
}
int re=;
while ()
{
it=s.lower_bound((Dat){,l,,});
if (it==s.end()||(*it).l>r) break;
Dat t=(*it);s.erase(it);
re=Seg::merge(re,t.nd);
}
//printf("%d\n",re);
return re;
}
int main()
{
Seg::init();
n=read();m=read();
for (int i=;i<=n;i++)
{
int t=;
Seg::update(t,,n,read());
//printf("%d\n",t);
s.insert((Dat){i,i,t,});
}
while (m--)
{
int op=read(),l=read(),r=read();
int t=split_node(l,r);
s.insert((Dat){l,r,t,op});
}
int q=read();
int t=split_node(q,q);
//printf("%d\n",t);
printf("%d\n",Seg::kth(,n,t,));
return ;
}
[HEOI2016/TJOI2016] 排序 解题报告(二分答案/线段树分裂合并+set)的更多相关文章
- 洛谷 P2824 [HEOI2016/TJOI2016]排序 解题报告
P2824 [HEOI2016/TJOI2016]排序 题意: 有一个长度为\(n\)的1-n的排列\(m\)次操作 \((0,l,r)\)表示序列从\(l\)到\(r\)降序 \((1,l,r)\) ...
- [BZOJ4552][TJOI2016&&HEOI2016]排序(二分答案+线段树/线段树分裂与合并)
解法一:二分答案+线段树 首先我们知道,对于一个01序列排序,用线段树维护的话可以做到单次排序复杂度仅为log级别. 这道题只有一个询问,所以离线没有意义,而一个询问让我们很自然的想到二分答案.先二分 ...
- Codeforces Round #276 (Div. 1) E. Sign on Fence (二分答案 主席树 区间合并)
链接:http://codeforces.com/contest/484/problem/E 题意: 给你n个数的,每个数代表高度: 再给出m个询问,每次询问[l,r]区间内连续w个数的最大的最小值: ...
- bzoj 4552 [Tjoi2016&Heoi2016]排序 (二分答案 线段树)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4552 题意: 给你一个1-n的全排列,m次操作,操作由两种:1.将[l,r]升序排序,2 ...
- BZOJ 4552 [Tjoi2016&Heoi2016]排序 | 二分答案 线段树
题目链接 题面 题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这 ...
- [BZOJ4552][Tjoi2016&Heoi2016]排序(二分答案+线段树)
二分答案mid,将>=mid的设为1,<mid的设为0,这样排序就变成了区间修改的操作,维护一下区间和即可 然后询问第q个位置的值,为1说明>=mid,以上 时间复杂度O(nlog2 ...
- 【BZOJ4552】【HEOI2016】排序 [二分答案][线段树]
排序 Time Limit: 60 Sec Memory Limit: 256 MB[Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列 ...
- 【Luogu】P2824排序(二分答案+线段树排序)
题目链接 震惊!两个线段树和一个线段树竟是50分的差距! 本题可以使用二分答案,二分那个位置上最后是什么数.怎么验证呢? 把原序列改变,大于等于mid的全部变成1,小于mid的全部变成0,之后线段树排 ...
- BZOJ4556 [Tjoi2016&Heoi2016]字符串 SA ST表 二分答案 主席树
原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ4556.html 题目传送门 - BZOJ4556 题意 给定一个长度为 $n$ 的字符串 $s$ . ...
随机推荐
- Binary Indexed Tree 总结
特点 1. 针对 数组连续子序列累加和 问题(需要进行频繁的 update.sum 操作): 2. 并非是树型结构,只是逻辑上层次分明: 3. 可以通过 填坑法 来理解: 4. 中心思想:每一个整数都 ...
- 一个基于React整套技术栈+Node.js的前端页面制作工具
pagemaker是一个前端页面制作工具,方便产品,运营和视觉的同学迅速开发简单的前端页面,从而可以解放前端同学的工作量.此项目创意来自网易乐得内部项目nfop中的pagemaker项目.原来项目的前 ...
- C语言整数类型在X86和X64下的字节大小
C声明 32位机器(X86) 64位机器(X64) char 1 1 short int 2 2 int 4 4 long int 4 8 long long int 8 8 char * 4 8 f ...
- js中,实现对键盘按键的监听:
<script> function keyUp(e) { var currKey=0,e=e||event; currKey=e.keyCode||e.which||e.charCode; ...
- 照片放大软件PhotoZoom怎么用?
做设计时,难免常常要上网找一些图片素材,但是有时候我们找到图片效果不尽人意,图片风格符合设计要求的的却图片太小,用photoshop放大后,图片的色块像素就出来了,效果极为不理想,这就要我们将图片进行 ...
- [USACO18JAN] MooTube (离线并查集)
题目大意:给你一棵边权树,定义两点间距离为它们唯一路径上的最小路权,求与某点距离不大于K(k为已知)的点的数量 带权并查集维护集合内元素总数 路和问题 都按权值大到小排序,枚举问题, 建权值不小于K的 ...
- Python多环境扩展管理
Python发展至今,版本众多,在使用过程中经常遇到第三方库依赖的Python版本和系统Python版本不一致的情况.同时又因系统底层需调用当前版本Python,所以不能随意变更当前系统Python版 ...
- S3C6410 LCD Overlay Test Program
测试了一下S3C6410 LCD控制器提供的Overlay功能,写了一个简单的test,主要用于实现FB0和FB1之间的Overlay操作.内核选项的Windows至少要为2. 具体支持如下操作:1 ...
- Spring Boot 启动的时候遇到 java.lang.ClassNotFoundException: ch.qos.logback.classic.Level
在刚开始接触spring boot的时候,想创建一个Hello World 的project. 但是创建完之后,Run as 'Spring Boot APP'的时候遇到这个错误. Level类存在于 ...
- 旋转VR相机不头晕:一个反直觉的发现
旋转VR相机不头晕:一个反直觉的发现 本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/deta ...