BZOJ 4552 [Tjoi2016&Heoi2016]排序 | 二分答案 线段树
题目链接
题面
题目描述
在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他。这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:
1:(0,l,r)表示将区间[l,r]的数字升序排序
2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q位置上的数字。输入输出格式
输入格式:
输入数据的第一行为两个整数n和m。n表示序列的长度,m表示局部排序的次数。
1 <= n, m <= 10^5第二行为n个整数,表示1到n的一个全排列。接下来输入m行,每一行有三个整数op, l, r, op为0代表升序排序,op为1代表降序排序, l, r 表示排序的区间。
最后输入一个整数q,q表示排序完之后询问的位置, 1 <= q <= n。1 <= n <= 10^5,1 <= m <= 10^5输出格式:
输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第q位置上的数字。
题解
这道题好有趣 = =
正解居然是二分答案!
因为最后要询问的只有一个位置,所以直接二分这个位置上的数。
然后修改一下原数组——原数组中比二分的答案大的数都改为1, 小于等于的都改为0。
然后用线段树模拟整个排序过程。把一个01序列的区间排序是很简单的——数出区间内有多少个1, 然后(如果是顺序排序)把右面对应的几个改成1, 剩下的改成0。
模拟执行一遍之后就可以知道实际上排完序后询问位置上的数应该比你二分的答案大还是小了,继续二分即可。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
#define space putchar(' ')
#define enter putchar('\n')
typedef long long ll;
using namespace std;
template <class T>
void read(T &x){
char c;
bool op = 0;
while(c = getchar(), c < '0' || c > '9')
if(c == '-') op = 1;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar('0' + x % 10);
}
const int N = 100005;
int n, m, a[N], val[N], pos, op[N], ql[N], qr[N];
int data[4*N];
bool lazy[4*N], tag[4*N];
void build(int k, int l, int r){
tag[k] = 0;
if(l == r) return (void)(data[k] = val[l]);
int mid = (l + r) >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
data[k] = data[k << 1] + data[k << 1 | 1];
}
void pushdown(int k, int l, int r){
if(!tag[k]) return;
int mid = (l + r) >> 1;
data[k << 1] = lazy[k] * (mid - l + 1);
data[k << 1 | 1] = lazy[k] * (r - mid);
lazy[k << 1] = lazy[k << 1 | 1] = lazy[k];
tag[k << 1] = tag[k << 1 | 1] = 1;
tag[k] = 0;
}
void change(int k, int l, int r, int ql, int qr, int x){
if(ql <= l && qr >= r) return (void)(data[k] = x * (r - l + 1), lazy[k] = x, tag[k] = 1);
pushdown(k, l, r);
int mid = (l + r) >> 1;
if(ql <= mid) change(k << 1, l, mid, ql, qr, x);
if(qr > mid) change(k << 1 | 1, mid + 1, r, ql, qr, x);
data[k] = data[k << 1] + data[k << 1 | 1];
}
int query(int k, int l, int r, int ql, int qr){
if(ql <= l && qr >= r) return data[k];
pushdown(k, l, r);
int mid = (l + r) >> 1, ret = 0;
if(ql <= mid) ret += query(k << 1, l, mid, ql, qr);
if(qr > mid) ret += query(k << 1 | 1, mid + 1, r, ql, qr);
return ret;
}
int main(){
read(n), read(m);
for(int i = 1; i <= n; i++) read(a[i]);
for(int i = 1; i <= m; i++)
read(op[i]), read(ql[i]), read(qr[i]);
read(pos);
int l = 1, r = n, mid;
while(l < r){
mid = (l + r) >> 1;
for(int i = 1; i <= n; i++)
val[i] = a[i] > mid;
build(1, 1, n);
for(int i = 1; i <= m; i++){
int cnt = query(1, 1, n, ql[i], qr[i]);
if(op[i]){
if(cnt) change(1, 1, n, ql[i], ql[i] + cnt - 1, 1);
if(ql[i] + cnt <= qr[i]) change(1, 1, n, ql[i] + cnt, qr[i], 0);
}
else{
if(cnt) change(1, 1, n, qr[i] - cnt + 1, qr[i], 1);
if(qr[i] - cnt >= ql[i]) change(1, 1, n, ql[i], qr[i] - cnt, 0);
}
}
if(query(1, 1, n, pos, pos)) l = mid + 1;
else r = mid;
}
write(l), enter;
return 0;
}
BZOJ 4552 [Tjoi2016&Heoi2016]排序 | 二分答案 线段树的更多相关文章
- bzoj 4552 [Tjoi2016&Heoi2016]排序 (二分答案 线段树)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4552 题意: 给你一个1-n的全排列,m次操作,操作由两种:1.将[l,r]升序排序,2 ...
- [BZOJ4552][TJOI2016&&HEOI2016]排序(二分答案+线段树/线段树分裂与合并)
解法一:二分答案+线段树 首先我们知道,对于一个01序列排序,用线段树维护的话可以做到单次排序复杂度仅为log级别. 这道题只有一个询问,所以离线没有意义,而一个询问让我们很自然的想到二分答案.先二分 ...
- bzoj 4552 [Tjoi2016&Heoi2016]排序——二分答案
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4552 二分答案,把 >= mid 的设成1.< mid 的设成0,之后排序就变成 ...
- bzoj 4552: [Tjoi2016&Heoi2016]排序——二分+线段树
Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这 ...
- BZOJ 4552: [Tjoi2016&Heoi2016]排序
4552: [Tjoi2016&Heoi2016]排序 Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 579 Solved: 322[Sub ...
- BZOJ 4552 [Tjoi2016&Heoi2016]排序 ——线段树 二分答案
听说是BC原题. 好题,二分答案变成01序列,就可以方便的用线段树维护了. 然后就是区间查询和覆盖了. #include <map> #include <cmath> #inc ...
- BZOJ 4552: [Tjoi2016&Heoi2016]排序 线段树 二分
目录 此代码是个假代码,只能糊弄luogu,以后再改,路过大佬也可以帮一下辣 update 10.6 此代码是个假代码,只能糊弄luogu,以后再改,路过大佬也可以帮一下辣 /* //fang zhi ...
- bzoj 4552: [Tjoi2016&Heoi2016]排序【二分+线段树】
二分值mid,然后把>=mid的赋值为1,其他赋值为0,每次排序就是算出区间内01的个数,然后分别把0和1放到连续的一段内,这些都可以用线段树来维护 二分的判断条件是操作完之后q位置上是否为1 ...
- BZOJ 4552 [Tjoi2016&Heoi2016]排序 线段树的分裂和合并
https://www.lydsy.com/JudgeOnline/problem.php?id=4552 https://blog.csdn.net/zawedx/article/details/5 ...
随机推荐
- 事务,acid,cap,paxos随笔
事务ACID四个特性: A:原子性(Atomicity)C:一致性(Consistency)I:隔离性(Isolation)D:持久性(Durability) 原子性:语句要么全执行,要么全不执行,是 ...
- “论 ofo 是如何影响今日头条发展的”
近段时间, ofo 小黄车押金难退的消息频频曝出.尽管 OFO 已经宣布押金只能在线上退还,但是线上退押金也难,因此很多的用户还是选择到 ofo 北京总部“要个说法”.记者昨天在现场发现,位于北京中关 ...
- C#_XML与Object转换
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.X ...
- CentOS6下OpenLDAP+PhpLdapAdmin基本安装及主从/主主高可用模式部署记录
下面测试的部署机ip地址为:192.168.10.2051)yum安装OpenLDAP [root@openldap-server ~]# yum install openldap openldap- ...
- linux下rsync和tar增量备份梳理
前面总结过一篇全量备份/增量备份/差异备份说明,下面介绍下linux下rsync和tar两种增量备份的操作记录: 1)rsync备份 rsync由于本身的特性,在第一次rsync备份后,以后每次都只是 ...
- LVS负载均衡-基础知识梳理
一. 集群的概念 服务器集群简称集群是一种服务器系统,它通过一组松散集成的服务器软件和/或硬件连接起来高度紧密地协作完成计算工作.在某种意义上,他们可以被看作是一台服务器.集群系统中的单个服务器通常称 ...
- Python迭代器(Iterator)
概述 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退. 延迟计算或惰性求值 (Lazy evaluation) 迭代器不要求你 ...
- 第六周分析Linux内核创建一个新进程的过程
潘恒 原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 task_struct结构: ...
- linux内核分析实践二学习笔记
Linux实践二--内核模块的编译 标签(空格分隔): 20135328陈都 理解内核的作用 Linux内核[kernel]是整个操作系统的最底层,它负责整个硬件的驱动,以及提供各种系统所需的核心功能 ...
- 第三次Sprint-最后冲刺
由于一些原因,导致我和汝婷被退队了.因此我们是从上星期重新开始做系统. 陈汝婷单独负责: 1.用户输入题目数: 2.限制题数: 3.自动生成用户需要题目数的题目: 4.计时 练丽云单独: 1.异常处理 ...