Luogu P2824 排序 题解 [ 紫 ] [ 线段树 ] [ 二分 ] [ adhoc ]
排序:二分线段树神仙好题。
trick
我们可以二分值域,然后把大于等于它的数标记成 \(1\),其他标记为 \(0\)(有些题需要标记成 \(-1\) ),然后根据这个来 check 方案是否可行,这通常通过判断某个数是否是 \(1\) 来实现。本质上其实就是 check 大于等于它的数能否成为答案(大于等于它的数为 \(1\))。常用于查找中位数、第 \(k\) 个数,以及大小关系只注重两种(比如只区分大于 \(7\) 和小于 \(7\) ,而大于 \(7\) 的数之间的大小无关的情况)。
运用到和这道题类似的 trick 的题还有这几道:最大战力 ,Median Pyramid Hard。
思路
知道这个 trick 之后这题就迎刃而解了。
因为这是个排列,所以我们直接二分 \(1\) 到 \(n\) 中的数,把大于等于它的数标记成 \(1\) ,其余为 \(0\) 。
在 check 中,我们用线段树维护一个 \(01\) 序列,处理好区间查询与区间的 \(01\) 排序。
最后 check 时查询线段树上的第 \(q\) 项是多少,如果是 \(1\) 就说明取的值小于等于答案(因为此时标记为 \(1\) 的太多了),否则就说明我们取的值比答案大(标记为 \(1\) 的太少了)。最后输出最后一个第 \(q\) 位是 \(1\) 的数即可。
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
const int N=100005;
int n,m,a[N],ql[N],qr[N],qop[N],q;
struct node{
int l,r;
int sum1,sum0;
int tag;
}tr[4*N];
void pushup(node &p,node ls,node rs)
{
p.sum1=ls.sum1+rs.sum1;
p.sum0=ls.sum0+rs.sum0;
}
void pd(int p,int op)
{
node &t=tr[p];
if(op==0)
{
t.sum1=0;
t.sum0=t.r-t.l+1;
t.tag=0;
}
if(op==1)
{
t.sum0=0;
t.sum1=t.r-t.l+1;
t.tag=1;
}
}
void pushdown(int p)
{
if(tr[p].tag==0)
{
pd(lc,0);
pd(rc,0);
}
if(tr[p].tag==1)
{
pd(lc,1);
pd(rc,1);
}
tr[p].tag=-1;
}
void build(int p,int ln,int rn,int x)
{
tr[p]={ln,rn,a[ln]>=x,a[ln]<x,-1};
if(ln==rn)return;
int mid=(ln+rn)>>1;
build(lc,ln,mid,x);
build(rc,mid+1,rn,x);
pushup(tr[p],tr[lc],tr[rc]);
}
void update(int p,int ln,int rn,int op)
{
if(ln<=tr[p].l&&tr[p].r<=rn)
{
pd(p,op);
return;
}
pushdown(p);
int mid=(tr[p].l+tr[p].r)>>1;
if(ln<=mid)update(lc,ln,rn,op);
if(rn>=mid+1)update(rc,ln,rn,op);
pushup(tr[p],tr[lc],tr[rc]);
}
node query(int p,int ln,int rn)
{
if(ln<=tr[p].l&&tr[p].r<=rn)return tr[p];
int mid=(tr[p].l+tr[p].r)>>1;
pushdown(p);
if(rn<=mid)return query(lc,ln,rn);
if(ln>=mid+1)return query(rc,ln,rn);
node tmp;
pushup(tmp,query(lc,ln,rn),query(rc,ln,rn));
return tmp;
}
bool check(int x)
{
build(1,1,n,x);
// for(int i=1;i<=n;i++)cout<<query(1,i,i).sum1<<' ';
// cout<<endl;
for(int i=1;i<=m;i++)
{
node res=query(1,ql[i],qr[i]);
int sum1=res.sum1,sum0=res.sum0;
if(qop[i]==0)
{
if(ql[i]<=ql[i]+sum0-1)update(1,ql[i],ql[i]+sum0-1,0);
if(ql[i]+sum0<=qr[i])update(1,ql[i]+sum0,qr[i],1);
}
else
{
if(ql[i]<=ql[i]+sum1-1)update(1,ql[i],ql[i]+sum1-1,1);
if(ql[i]+sum1<=qr[i])update(1,ql[i]+sum1,qr[i],0);
}
// for(int i=1;i<=n;i++)cout<<query(1,i,i).sum1<<' ';
// cout<<endl;
}
return (query(1,q,q).sum1==1);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++)cin>>qop[i]>>ql[i]>>qr[i];
cin>>q;
int l=1,r=n,mid;
while(l<r)
{
mid=(l+r+1)>>1;
if(check(mid))l=mid;
else r=mid-1;
// cout<<"l="<<l<<" ; r="<<r<<" ; mid="<<mid<<endl;
// for(int i=1;i<=n;i++)cout<<query(1,i,i).sum1<<' ';
// cout<<endl<<endl;
}
cout<<l;
return 0;
}
Luogu P2824 排序 题解 [ 紫 ] [ 线段树 ] [ 二分 ] [ adhoc ]的更多相关文章
- [火星补锅] 水题大战Vol.2 T1 && luogu P1904 天际线 题解 (线段树)
前言: 当时考场上并没有想出来...后来也是看了题解才明白 解析: 大家(除了我)都知道,奇点和偶点会成对出现,而出现的前提就是建筑的高度突然发生变化.(这个性质挺重要的,我之前没看出来) 所以就可以 ...
- Luogu[YNOI2019]排序(DP,线段树)
要最优?就要一步到位,不能做"马后炮",走"回头路",因此将序列映射到一个假定最优序列,发现移动原序列等价于删除原序列元素,以便生成最大不下降子序列.可线段树维 ...
- Codeforces Gym 100231B Intervals 线段树+二分+贪心
Intervals 题目连接: http://codeforces.com/gym/100231/attachments Description 给你n个区间,告诉你每个区间内都有ci个数 然后你需要 ...
- hdu4614 线段树+二分 插花
Alice is so popular that she can receive many flowers everyday. She has N vases numbered from 0 to N ...
- 洛谷$P2824\ [HEOI2016/TJOI2016]$ 排序 线段树+二分
正解:线段树+二分 解题报告: 传送门$QwQ$ 昂着题好神噢我$jio$得$QwQQQQQ$,,, 开始看到长得很像之前考试题的亚子,,,然后仔细康康发现不一样昂$kk$,就这里范围是$[1,n]$ ...
- 2021.12.09 [HEOI2016/TJOI2016]排序(线段树+二分,把一个序列转换为01串)
2021.12.09 [HEOI2016/TJOI2016]排序(线段树+二分,把一个序列转换为01串) https://www.luogu.com.cn/problem/P2824 题意: 在 20 ...
- Codeforces Gym 100803G Flipping Parentheses 线段树+二分
Flipping Parentheses 题目连接: http://codeforces.com/gym/100803/attachments Description A string consist ...
- 洛谷P4344 脑洞治疗仪 [SHOI2015] 线段树+二分答案/分块
!!!一道巨恶心的数据结构题,做完当场爆炸:) 首先,如果你用位运算的时候不小心<<打成>>了,你就可以像我一样陷入疯狂的死循环改半个小时 然后,如果你改出来之后忘记把陷入死循 ...
- luogu4422 [COCI2017-2018#1] Deda[线段树二分]
讨论帖:线段树二分的题..我还考场切过..白学 这题我一年前的模拟赛考场还切过,现在就不会了..好菜啊. 显然直接线段树拆成$\log n$个区间,然后每个区间在进行线段树二分即可. UPD:复杂度分 ...
- bzoj4399 魔法少女LJJ 线段树合并+线段树二分+并查集
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4399 题解 毒瘤题 \(9\) 种操作还有支持动态图的连通性 仔细读题 $ c<=7$. ...
随机推荐
- Django之model外键
外键和表关系 外键: 在MySQL中,表有两种引擎,一种是InnoDB,另外一种是myisam.如果使用的是InnoDB引擎,是支持外键约束的.外键的存在使得ORM框架在处理表关系的时候异常的强大.因 ...
- Linux命令之ncdu
简介 Ncdu - NCurses Disk Usage Ncdu 是一个带有 ncurses 接口的磁盘使用分析器. 它旨在在您没有完整图形设置可用的远程服务器上查找空间占用,但即使在常规桌面系统上 ...
- Lambda【1】-- List相关Lambda表达式使用(上篇)
Lambda在jdk1.8里面已经很好用了,在这里不讲底层的实现,只有简单的用法,会继续补全. 首先一个list我们要使用lambda的话,需要使用它的stream()方法,获取流,才能使用后续的方法 ...
- 3-XSS渗透与防御
1.HTTP协议回顾 XSS又名跨站脚本攻击 web页面登陆页面,往往有一个"记住密码"功能 ---> Cookie 1.1 HTTP流程 1.2 HTTP特点: 请求应答模 ...
- Javascript之常用尺寸、位置获取
标签: js 缘起 平时在开发中或多或少需要去获取元素尺寸,特此记录常用的尺寸获取方案. 常用相关尺寸 窗口可视尺寸 测试地址 /** * 获取窗口可视尺寸 */ function getWindow ...
- cockpit-325以及新版本安装和cockpit-files安装
查看版本cockpit可安装版本,发现最新的只有310版本 sudo yum list cockpit --showduplicates Last metadata expiration check: ...
- Docker 使用 buildx 构建多种系统架构支持的镜像
在 Docker 19.03+ 版本中可以使用 docker buildx build 命令使用 BuildKit 构建镜像.该命令支持 --platform 参数可以同时构建支持多种系统架构的 Do ...
- Spring注解之-@ConditionalOnExpression表达式
@ConditionalOnExpression("'true") 当括号中的内容为true时,使用该注解的类被实例化,支持语法如下: @ConditionalOnExpressi ...
- springboot 多数据源(aop方式)
一.实现思路 在yml中定义多个数据源的配置,然后创建一个类DynamicDataSource去继承AbstractRoutingDataSource类 AbstractRoutingDataSour ...
- Qt编写安防视频监控系统55-布局方案
一.前言 这是近期对视频监控系统做得最大的改动,采用了QMainWindow停靠窗体悬停窗体,极大的增强了拓展性,主界面中的各种小模块全部采用了悬停模块,最后不同的工作模式.不同的运行状态(全屏状态. ...