题目
题解
二分q位置上的值mid,然后将所有大于mid的数记为1,小于等于mid的数记为0,用线段树维护区间的和
对于升序排列,及时将所有为1的数放在右边;降序排列就是将所有为0的数放在右边
最后判断出p位置为1还是0,若为1,则说明mid小于真实值,l=mid+1;若为0,则mid可能大于真实值(注意是可能),记录ans并r=mid
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 1000000
using namespace std;
int n,m,a[N],q;
int opt[N],cl[N],cr[N];
struct node{int l,r,sum,d;}T[N];
void pushup(int p){T[p].sum=T[p<<1].sum+T[p<<1|1].sum;}
void pushdown(int p)
{
if(T[p].d==-1) return;
T[p<<1].sum=(T[p<<1].r-T[p<<1].l+1)*T[p].d;
T[p<<1|1].sum=(T[p<<1|1].r-T[p<<1|1].l+1)*T[p].d;
T[p<<1].d=T[p<<1|1].d=T[p].d;
T[p].d=-1;
}
void build(int p,int x,int y,int v)
{
T[p].l=x;T[p].r=y;T[p].d=-1;
if(x==y) {T[p].sum=(a[x]>v);return;}
if(x<y)
{
int mid=(x+y)>>1;
build(p<<1,x,mid,v);
build(p<<1|1,mid+1,y,v);
pushup(p);
}
}
int query(int p,int x,int y)
{
int pl=T[p].l,pr=T[p].r;
if(pl==x&&pr==y) return T[p].sum;
pushdown(p);
int mid=(pl+pr)>>1;
if(y<=mid) return query(p<<1,x,y);
else if(x>mid) return query(p<<1|1,x,y);
else return query(p<<1,x,mid)+query(p<<1|1,mid+1,y);
}
void set(int p,int x,int y,int v)
{
int pl=T[p].l,pr=T[p].r;
if(pl==x&&pr==y)
{
T[p].sum=(T[p].r-T[p].l+1)*v;
T[p].d=v;
return;
}
pushdown(p);
int mid=(pl+pr)>>1;
if(y<=mid) set(p<<1,x,y,v);
else if(x>mid) set(p<<1|1,x,y,v);
else
{
set(p<<1,x,mid,v);
set(p<<1|1,mid+1,y,v);
}
pushup(p);//
}
bool check(int x)
{
build(1,1,n,x);//建树
for(int i=1;i<=m;i++)
{
int sum=query(1,cl[i],cr[i]);//先求出区间内大于x的个数
if(!opt[i])//升序
{
if(sum) set(1,cr[i]-sum+1,cr[i],1);//将1全部放右边
if(cr[i]-sum>=cl[i]) set(1,cl[i],cr[i]-sum,0);
}
else//降序
{
if(sum) set(1,cl[i],cl[i]+sum-1,1);//将1全部放左边
if(cl[i]+sum<=cr[i]) set(1,cl[i]+sum,cr[i],0);
}
}
return query(1,q,q)==0;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++) scanf("%d%d%d",&opt[i],&cl[i],&cr[i]);
scanf("%d",&q);
int l=1,r=n,mid,ans;
while(l<r)//二分p位置上的值
{
mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid;
else l=mid+1;
}
printf("%d",ans);
return 0;
}