题目描述

在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:

6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3
输出样例#1:

5

说明

河北省选2016第一天第二题。原题的时限为6s,但是洛谷上是1s,所以洛谷的数据中,对于30%的数据,有 n,m<=1000,对于100%的数据,有 n,m<=30000

  首先先声明一下,方法与Drench大佬的差不多,此篇题解填了一些解释,让代码更容易理解。(P.S.:我的代码不知道为啥在主站会T第10个点,在大牛分站交过了,能有大佬想到一些优化的方法当然是更好,这题解主要是介绍思路和代码含义)

SOLUTION:

First:主体思路:二分答案+线段树操作

Second:代码完成思路

  1.读入,存储数值

  2.二分第Q个值的答案

  3.线段树的操作

    1)建树 2)进行m次操作 3)找到第q个点二分答案

  4.二分left 和 right的更改

Third:线段树操作方法及能实现的原因

  1.二分出q,建树时大于等于q叶子节点为1,反之为0,然后利用线段树处理的sum(和)。

  2.对于第i步修改操作中,区间l-r中的和及大于等于q的个数(1),r-l+1为区间数的个数,求差就是小于q的个数(0)。

  3.求出l-r区间num0与num1个数后,若从小到大,则将l-(l+num0-1)赋值为0,(l+num0-r)赋值为1

     反之,则将l-(l+num1-1)赋值为1,(l+num1-r)赋值为0

  4.上述步骤重复m次后,就可以去查询第Q个数,如果为1说明二分出来的q大于等于答案,如果为0说明二分出来的q小于q

下面就是我的代码了,代码中有一些注释:

//It is coded by Ning_Mew on 10.17
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int read(){//读入优化
int x=;char ch=getchar();
while(ch<''||ch>'')ch=getchar();
while(ch>=''&&ch<='')x=(x*)+ch-'',ch=getchar();
return x;
}
int n,m,q,top=,tail,mid;
struct Operation{
int type,l,r;
}ope[+];//储存更改操作
struct Node{
int l,r,sum,lazy;
}node[+];//线段树储存
int a[+];//储存每个值
void build(int num,int nl,int nr){//建树
if(nl==nr){
node[num].l=nl; node[num].r=nr;
if(a[nl]>=mid)node[num].sum=;
else node[num].sum=;//对应Third中的第一点
return;
}
int nmid=(nl+nr)/;
build(num*,nl,nmid);
build(num*+,nmid+,nr);
node[num].l=nl; node[num].r=nr;
node[num].sum=(node[num*].sum+node[num*+].sum);
return;
}
void pushdown(int num,int nl,int nr){//下放lazy操作
if(node[num].lazy!=-){
node[num*].lazy=node[num].lazy;
node[num*+].lazy=node[num].lazy;//因为lazy是将这个节点中的数全部更改为0或1,所以lazy是直接被覆盖而不是+=
node[num*].sum=node[num].lazy*(node[num*].r-node[num*].l+);
node[num*+].sum=node[num].lazy*(node[num*+].r-node[num*+].l+);//同样因为是全被更改,所以直接覆盖
node[num].lazy=-;
}
return;
}
int count(int num,int nl,int nr,int ql,int qr){//求区间和
if(qr<nl||nr<ql)return ;
if(ql<=nl&&nr<=qr){
return node[num].sum;
}
pushdown(num,nl,nr);
int nmid=(nl+nr)/;
return count(num*,nl,nmid,ql,qr)+count(num*+,nmid+,nr,ql,qr);
}
void change(int num,int nl,int nr,int ql,int qr,int add){//区间更改
if(qr<nl||nr<ql||ql>qr)return;
if(ql<=nl&&nr<=qr){
node[num].sum=add*(node[num].r-node[num].l+);//被防止了lazy的点本身应已被更改,下次就只用下放lazy了
node[num].lazy=add;
return;
}
pushdown(num,nl,nr);
int nmid=(nl+nr)/;
change(num*,nl,nmid,ql,qr,add);
change(num*+,nmid+,nr,ql,qr,add);
node[num].sum=node[num*].sum+node[num*+].sum;//两个儿子节点已更改所以更新此节点sum值
return;
}
bool judge(){//检查q值
build(,,n);
for(int i=;i<=*n+;i++)node[i].lazy=-;
for(int i=;i<=m;i++){
int ll=ope[i].l,rr=ope[i].r;
int num1=count(,,n,ll,rr),num0;
num0=rr-ll+-num1;
if(ope[i].type==){//对应Third 3点
change(,,n,ll,ll+num0-,);
change(,,n,ll+num0,rr,);
}
else{
change(,,n,ll,ll+num1-,);
change(,,n,ll+num1,rr,);
}
}
if(count(,,n,q,q))return true;//1-->true
return false;//0-->false
}
int main(){
n=read();m=read();
for(int i=;i<=n;i++){
a[i]=read();
}
for(int i=;i<=m;i++){
ope[i].type=read();
ope[i].l=read();
ope[i].r=read();
}
q=read();
top=;tail=n+;
do{
mid=(top+tail)/;
if(judge()){top=mid;}
else {tail=mid;}
}while(top<tail-);
cout<<top;
return ;
}

题解就写到这啦,好好理解还是不难的~

【题解】 [HEOI2016]排序题解 (二分答案,线段树)的更多相关文章

  1. [BZOJ4552][Tjoi2016&Heoi2016]排序(二分答案+线段树)

    二分答案mid,将>=mid的设为1,<mid的设为0,这样排序就变成了区间修改的操作,维护一下区间和即可 然后询问第q个位置的值,为1说明>=mid,以上 时间复杂度O(nlog2 ...

  2. 【Luogu】P2824排序(二分答案+线段树排序)

    题目链接 震惊!两个线段树和一个线段树竟是50分的差距! 本题可以使用二分答案,二分那个位置上最后是什么数.怎么验证呢? 把原序列改变,大于等于mid的全部变成1,小于mid的全部变成0,之后线段树排 ...

  3. [BZOJ4552][TJOI2016&&HEOI2016]排序(二分答案+线段树/线段树分裂与合并)

    解法一:二分答案+线段树 首先我们知道,对于一个01序列排序,用线段树维护的话可以做到单次排序复杂度仅为log级别. 这道题只有一个询问,所以离线没有意义,而一个询问让我们很自然的想到二分答案.先二分 ...

  4. BZOJ 4552 [Tjoi2016&Heoi2016]排序 | 二分答案 线段树

    题目链接 题面 题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这 ...

  5. [HEOI2016/TJOI2016] 排序 解题报告(二分答案/线段树分裂合并+set)

    题目链接: https://www.luogu.org/problemnew/show/P2824 题目描述: 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在 ...

  6. bzoj 4552 [Tjoi2016&Heoi2016]排序 (二分答案 线段树)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4552 题意: 给你一个1-n的全排列,m次操作,操作由两种:1.将[l,r]升序排序,2 ...

  7. 【BZOJ4552】【HEOI2016】排序 [二分答案][线段树]

    排序 Time Limit: 60 Sec  Memory Limit: 256 MB[Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列 ...

  8. HDU 5649 DZY Loves Sorting(二分答案+线段树/线段树合并+线段树分割)

    题意 一个 \(1\) 到 \(n\) 的全排列,\(m\) 种操作,每次将一段区间 \([l,r]\) 按升序或降序排列,求 \(m\) 次操作后的第 \(k\) 位. \(1 \leq n \le ...

  9. YbtOJ#463-序列划分【二分答案,线段树,dp】

    正题 题目链接:https://www.ybtoj.com.cn/problem/463 题目大意 给出长度为\(n\)的序列\(A,B\).要求划分成若干段满足 对于任何\(i<j\),若\( ...

随机推荐

  1. 20155223 Exp8 WEB基础实践

    20155223 Exp8 WEB基础实践 基础问题回答 什么是表单? 表单是一个包含表单元素的区域. 表单元素是允许用户在表单中(比如:文本域.下拉列表.单选框.复选框等等)输入信息的元素. 表单使 ...

  2. mysql基础(三)——中级查询

    创建表 CREATE TABLE DEPT( DEPTNO ) PRIMARY KEY, DNAME ) , LOC ) ) ; ,'ACCOUNTING','NEW YORK'); ,'RESEAR ...

  3. elasticsearch同步mongodb--mongo connector的使用

    部署准备 python-3.6.4-amd64.exe mongodb-win32-x86_64-3.4.6-signed.msi  (如果已经安装可以忽略) 注意点! 之前我写的一篇文章用的是ela ...

  4. Katalon Studio学习笔记(二)——请求响应中文乱码解决方法

    Katalon Studio接口测试发现返回的中文消息是乱码,这是因为KS的编码格式是UTF-8,因此导致中文字体出现乱码.如下图所示: 在我们的系统中添加一个名字为JAVA_TOOL_OPTIONS ...

  5. pyinstaller将python编写的打卡程序打包成exe

    编写了一个简易的定时提醒下班打卡程序,python代码如下: #coding:utf-8 import time import datetime from tkMessageBox import * ...

  6. 关于python内存地址问题

    遇到一个朋友,给我提了一个问题:python中的两个相同的值,内存地址是否一样? 当时印象里有这样一句话:Python采用基于值的内存管理模式,相同的值在内存中只有一份 于是张嘴就说是一样的 朋友说不 ...

  7. Go语言实现数据结构(一)单链表

    1.基本释义 2.结构体设计 3.基本方法设计 4.Main函数测试 1. 基本释义 线性表包含两种存储方法:顺序存储结构和链式存储结构,其中顺序表的缺点是不便插入与删除数据:接下来我们重点实现基于G ...

  8. Linux 第六周实验

    姬梦馨 原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.进程控制块PCB——task_st ...

  9. Linux内核分析——第二周学习笔记20135308

    第二周 操作系统是如何工作的 第一节 函数调用堆栈 存储程序计算机:是所有计算机基础的框架 堆栈:计算机中基础的部分,在计算机只有机器语言.汇编语言时,就有了堆栈.堆栈机制是高级语言可以运行的基础. ...

  10. Day Four

    站立式会议 站立式会议内容总结 442 今天:整合主页两个部分的逻辑代码,主页及其跳转基本完成 遇到的问题:无 明天:阅读图书界面逻辑部分完成 331 今天:学习java反射添加类数据到数据库 遇到问 ...