处理方式——[HE/TJOI2016]排序
https://www.luogu.com.cn/problem/P2824
从这个题的二分答案确实不太好想。主要地,这题有两个切入点:①区间排序②离线单点询问。
从①的话,可能可以想到对 01 序列的排序可以用线段树维护;
从②的话,可能可以想到二分答案是否大于等于 \(x\),从而人为造成单调性。
所以本题的做法是二分 \(a_q\) 是否 \(\ge x\),并将序列中 \(\ge x,<x\) 的分别设为 \(1,0\),对新序列(是个 01 序列)的排序只需要支持区间和、区间赋值操作。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
inline int read(){
register char ch=getchar();register int x=0;
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
int n,m,p,a[N],b[N],t[N<<2],tag[N<<2];
struct J{int op,l,r;}q[N];
void pushup(int k){
t[k]=t[k<<1]+t[k<<1|1];
}
void pushdown(int l,int r,int k){
if(tag[k]<0)return;
int mid=l+r>>1;
tag[k<<1]=tag[k],tag[k<<1|1]=tag[k];
t[k<<1]=(mid-l+1)*tag[k],t[k<<1|1]=(r-mid)*tag[k];
tag[k]=-1;
}
void build(int l,int r,int k){
tag[k]=-1;
if(l==r){t[k]=b[l];return;}
int mid=l+r>>1;
build(l,mid,k<<1),build(mid+1,r,k<<1|1);
pushup(k);
}
void chg(int L,int R,int v,int l,int r,int k){
if(L<=l&&r<=R){t[k]=(r-l+1)*v;tag[k]=v;return;}
pushdown(l,r,k);
int mid=l+r>>1;
if(L<=mid)chg(L,R,v,l,mid,k<<1);
if(R>mid)chg(L,R,v,mid+1,r,k<<1|1);
pushup(k);
}
int ask(int L,int R,int l,int r,int k){
if(L<=l&&r<=R)return t[k];
pushdown(l,r,k);
int mid=l+r>>1,ans=0;
if(L<=mid)ans+=ask(L,R,l,mid,k<<1);
if(R>mid)ans+=ask(L,R,mid+1,r,k<<1|1);
return ans;
}
bool check(int x){
for(int i=1;i<=n;i++)b[i]=a[i]>=x;
build(1,n,1);
for(int i=1;i<=m;i++){
int t=ask(q[i].l,q[i].r,1,n,1);
if(!q[i].op){
if(t)chg(q[i].r-t+1,q[i].r,1,1,n,1);
if(q[i].r-t>=q[i].l)chg(q[i].l,q[i].r-t,0,1,n,1);
}
else {
if(t)chg(q[i].l,q[i].l+t-1,1,1,n,1);
if(q[i].l+t<=q[i].r)chg(q[i].l+t,q[i].r,0,1,n,1);
}
}
return ask(p,p,1,n,1);
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=m;i++)q[i].op=read(),q[i].l=read(),q[i].r=read();
p=read();
int L=0,R=n+1,mid;
while(L<R-1){
mid=L+R>>1;
if(check(mid))L=mid;
else R=mid;
}
cout<<L;
}