[HEOI2016/TJOI2016]排序

题目链接

Solution

考虑离线回答这个问题,由于只问我们第 \(q\) 位,我们很自然想到枚举一个阈值,将大于等于它的变成 \(1\),小于它的变成 \(0\),然后对这个 01 序列进行排序操作。有单调性,举个栗子:

假设知道一个排完序后的序列 \(3,1,2,4,5\),现询问第 \(3\) 个位置。
若选 \(1\) 作为阈值,排序好的 01 序列为 \(1,1,1,1,1\),第 \(3\) 个位置为 \(1\)
若选 \(2\) 作为阈值,排序好的 01 序列为 \(1,0,1,1,1\),第 \(3\) 个位置为 \(1\)
若选 \(3\) 作为阈值,排序好的 01 序列为 \(1,0,0,1,1\),第 \(3\) 个位置为 \(0\)
若选 \(4\) 作为阈值,排序好的 01 序列为 \(0,0,0,1,1\),第 \(3\) 个位置为 \(0\)
若选 \(5\) 作为阈值,排序好的 01 序列为 \(0,0,0,0,1\),第 \(3\) 个位置为 \(0\)

可以发现,最大的满足第 \(q\) 个位置为 \(1\) 的阈值就是答案。若阈值偏小,则该位置为 \(1\);若阈值偏大,则该位置为 \(0\)。所以满足单调性,二分答案即可。

还有一个问题,如何快速对 01 序列排序。我们只需要知道这个序列中 \(1\) 的个数,再根据升序还是降序,决定这几个 \(1\) 放最前还是最后。用线段树进行区间求和以及区间修改即可。
注意特判全是 \(0\)\(1\) 的情况,不然区间修改会 RE。

Code

#include<bits/stdc++.h>
#define ls p*2
#define rs p*2+1
using namespace std;
void read(int &x)
{
	char ch=getchar();
	int r=0,w=1;
	while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
	while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
	x=r*w;
}
const int N=1e5+7;
int a[N],n,m,q,b[N],sum[N*4],lazy[N*4],size[N*4],ans;
struct sbt
{
	int op,l,r;
}qt[N];
void update(int p)
{
	sum[p]=sum[ls]+sum[rs];
	size[p]=size[ls]+size[rs];
}
void pushdown(int p)
{
	lazy[ls]=lazy[p];
	sum[ls]=size[ls]*lazy[p];
	lazy[rs]=lazy[p];
	sum[rs]=size[rs]*lazy[p];
	lazy[p]=-1;
}
void build(int p,int l,int r)
{
	lazy[p]=-1;
	if(l==r)
	{
		sum[p]=b[l];
		size[p]=1;
		return;
	}
	int mid=l+r>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	update(p);
}
void change(int p,int l,int r,int x,int y,int k)
{
	if(x<=l&&r<=y)
	{
		lazy[p]=k;
		sum[p]=size[p]*k;
		return;
	}
	if(lazy[p]!=-1)pushdown(p);
	int mid=l+r>>1;
	if(x<=mid)change(ls,l,mid,x,y,k);
	if(y>mid)change(rs,mid+1,r,x,y,k);
	update(p);
}
void query(int p,int l,int r,int x,int y)
{
	if(x<=l&&r<=y)
	{
		ans+=sum[p];
		return;
	}
	if(lazy[p]!=-1)pushdown(p);
	int mid=l+r>>1;
	if(x<=mid)query(ls,l,mid,x,y);
	if(y>mid)query(rs,mid+1,r,x,y);
}
bool check(int x)
{
	for(int i=1;i<=n;i++)
		b[i]=a[i]>=x?1:0;
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		int op=qt[i].op,l=qt[i].l,r=qt[i].r;
		ans=0;
		query(1,1,n,l,r);
		if(ans==r-l+1||ans==0)continue;
		if(!op)
		{
			change(1,1,n,l,r-ans,0);
			change(1,1,n,r-ans+1,r,1);
		}
		else
		{
			change(1,1,n,l,l+ans-1,1);
			change(1,1,n,l+ans,r,0);
		}
	}
	ans=0;
	query(1,1,n,q,q);
	return ans==1;
}
int main()
{
	read(n);read(m);
	for(int i=1;i<=n;i++)
		read(a[i]);
	for(int i=1;i<=m;i++)
		read(qt[i].op),read(qt[i].l),read(qt[i].r);
	read(q);
	int l=1,r=n,ans;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(check(mid))ans=mid,l=mid+1;
		else r=mid-1;
	}
	cout<<ans;
	return 0;
}
posted @ 2022-08-19 17:55  Epoch_L  阅读(25)  评论(0编辑  收藏  举报