TJOI 2016 排序
题解
这是一道二分答案的好题(感觉藏得很深)。
题目要求我们局部排序 m m m次,然后求第 q q q个位置的值。显然如果我们暴力模拟的话时间复杂度最坏为 O ( n 2 l o g n ) O(n^2logn) O(n2logn)。
那么考虑能否加快排序速度。
显然如果序列只有 0 , 1 0,1 0,1的话,我们可以利用线段树区间求和,区间赋值,做到 O ( l o g n ) O(logn) O(logn)排序一段区间。
那么如果题目为 0 , 1 0,1 0,1序列,这样的时间复杂度为 O ( m l o g n ) O(mlogn) O(mlogn)。
现在序列不是 0 , 1 0,1 0,1序列,考虑二分答案的套路:二分一个 m i d mid mid,然后把 a [ i ] > = m i d a[i]>=mid a[i]>=mid的值改为 1 1 1, a [ i ] < m i d a[i]<mid a[i]<mid的值改为 0 0 0。
再做一遍,看 q q q位置上是否为 1 1 1,若为 1 1 1则调整往右二分,为 0 0 0往左二分。
是否满足单调性?因为 m i d mid mid越小, 1 1 1的个数越多,那么如果二分到 x x x时第 q q q个为 1 1 1,那么二分比 x x x小的数第 q q q个位置上一样是 1 1 1,
二分比 x x x大的数则不一定,所以满足单调性。
C o d e \mathcal{Code} Code
/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月14日 星期一 16时30分00秒
*******************************/
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=1e5+10;
int n,m,k,l[maxn],r[maxn],opt[maxn],p[maxn],a[maxn];
/*{{{线段树*/
namespace SegmentTree{
int tr[maxn*4],lazy[maxn*4];//tr[k]表示区间下1的个数
void update(int k) { tr[k]=tr[k<<1]+tr[k<<1|1]; }
void add(int k,int l,int r,int val)
{
tr[k]=(r-l+1)*val;
lazy[k]=val;
}
void pushdown(int k,int l,int r,int mid)
{
if(lazy[k]==-1) return;
add(k<<1,l,mid,lazy[k]);
add(k<<1|1,mid+1,r,lazy[k]);
lazy[k]=-1;
}
void build(int k,int l,int r)
{
if(l==r)
{
tr[k]=a[l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
lazy[k]=-1;
update(k);
}
void modify(int k,int l,int r,int x,int y,int val)
{
if(l>=x && r<=y) return add(k,l,r,val);
if(l>y || r<x ) return;
int mid=(l+r)>>1;
pushdown(k,l,r,mid);
modify(k<<1,l,mid,x,y,val); modify(k<<1|1,mid+1,r,x,y,val);
update(k);
}
int query(int k,int l,int r,int x,int y)
{
if(l>=x && r<=y) return tr[k];
if(l>y || r<x) return 0;
int mid=(l+r)>>1;
pushdown(k,l,r,mid);
return (query(k<<1,l,mid,x,y)+query(k<<1|1,mid+1,r,x,y));
}
};
/*}}}*/
bool check(int mid)
{
for(int i=1;i<=n;i++)
a[i]=(p[i]>=mid);
SegmentTree::build(1,1,n);
for(int i=1;i<=m;i++)
{
int s=SegmentTree::query(1,1,n,l[i],r[i]),len=r[i]-l[i];
if(opt[i]==0)
{
SegmentTree::modify(1,1,n,l[i],l[i]+len-s,0);
SegmentTree::modify(1,1,n,l[i]+len-s+1,r[i],1);
}
else
{
SegmentTree::modify(1,1,n,l[i],l[i]+s-1,1);
SegmentTree::modify(1,1,n,l[i]+s,r[i],0);
}
}
return SegmentTree::query(1,1,n,k,k);
}
int main()
{
/*freopen("p2824.in","r",stdin);*/
/*freopen("p2824.out","w",stdout);*/
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>p[i];
for(int i=1;i<=m;i++)
cin>>opt[i]>>l[i]>>r[i];
cin>>k;
int z=1,y=n,mid,ans;
while(z<=y)
{
mid=(z+y)>>1;
if(check(mid))
ans=mid,z=mid+1;
else
y=mid-1;
}
cout<<ans<<endl;
return 0;
}