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$. ...
随机推荐
- 抓包工具之Fiddler(详解)
Fiddle简介 Fiddler是最强大最好用的Web调试工具之一,它能记录所有客户端和服务器的http和https请求,允许你监视,设置断点,甚至修改输入输出数据,Fiddler包含了一个强大的基于 ...
- Linux之buffer/cache
buffers和cached解释 =========================================================缓存(cached)是把读取过的数据保存起来,重新读 ...
- php调试工具之firePHP
让PHP可以像js一样利用浏览器的控制台调试代码 ------- FirePHP(http://www.firephp.org/) 1. 安装FirePHP插件 在Chrome浏览器的应用商店中,搜 ...
- ubuntu 下的 nslookup 命令利用 127.0.0.53 查询主机名失败,而使用网关则正常的问题
遇到一个奇怪的问题,ubuntu 下使用 KRDC 远程访问局域网主机时,连接主机名失败,使用 ip 则正常.通过 nslookup 命令发现,局域网主机名没有被正确解析(使用的是默认的 127.0. ...
- 基于.NetCore开发博客项目 StarBlog - (32) 第一期完结
前言 本文记录一下 StarBlog 项目的当前状态与接下来 v2 版本的开发规划. StarBlog 项目从 2022 年开始至今已经 2 年多了,本来早就该给第一期做个小结的,但这种博客类型的项目 ...
- ELSTICSEARCH 将数据从热节点转到冷节点
在ES查询时,有些数据是热点数据,这些数据是需要需要更好的机器配置的节点,当数据变得不是那么重要的时候,我们需要转到配置相对低的节点上. 比如热点新闻专题,我们需要更好的机器,热点过后,这些数据不再需 ...
- oracle中id自增长(转)
首先,你要有一张表! CREATE TABLE example( ID Number(4) NOT NULL PRIMARY KEY, NAME VARCHAR(25), PHONE VARCHAR( ...
- 高精度计算库math.js使用踩坑记
前情 最近在做一个后端需求,需求中需要前端做一些金额数字计算,前端对于小数的计算一直都有精度问题,如0.1+0.2计算的结果并不是0.3,而是0.30000000000000004,于是引入高精度计算 ...
- 上位机与MES数据交互的常用方案
一.前言 随着工业自动化水平的不断提高,我们在做上位机开发的时候,会经常涉及到与MES进行数据交互. 上位机与MES(Manufacturing Execution System,制造执行系统)之间的 ...
- 如何判断平台是x86还是arm
case $(uname -m) in x86_64) echo x86;; aarch64) echo arm;; esac ref 上面的代码片改自这里 https://stackoverflow ...