P7514-[省选联考2021A/B卷]卡牌游戏【贪心】

正题

题目链接:https://www.luogu.com.cn/problem/P7514


题目大意

给出(n)个卡牌有(a_i/b_i),开始都是(a_i)朝上,将不超过(m)张卡牌变为(b_i)面朝上,使得朝上的数字中最大值减去最小值最小。

(3leq nleq 10^6,1leq m<n,1leq a_i,b_ileq 10^9)


解题思路

虽然数据比较水,但是题目也是一道比较水的贪心题。

先离散化然后考虑暴力点的想法。枚举最大值和最小值(l,r),那么对于(a_i)([l,r])之间的自然是不理,在之外的一定需要翻,如果翻转后存在(b_i)不在([l,r])之间,那么显然行不通。

这样我们就可以(O(n^2))了。

不难发现假设([l,r])行的通那么([l,r+1])用原来的方法一定也行得通,所以对于(r)递增,(l)也是不降的,双指针维护一下就好了。

时间复杂度(O(nlog n))


code

#include<cstdio>
#include<cstring>
#include<algorithm> 
#include<cctype>
using namespace std;
const int N=2e6+10;
int n,m,ans,a[N],b[N],c[N],mx[N],mi[N],nx[N],ni[N],pos[N];
int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
bool cmp(int x,int y)
{return a[x]<a[y];}
bool check(int l,int r){
	int L=pos[l],R=pos[r+1]-1;
	int k=m-(n-R)-(L-1);
	if(k<0)return 0;
	if(mx[L-1]>r||mi[L-1]<l)return 0;
	if(nx[R+1]>r||ni[R+1]<l)return 0;
	return 1;
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=2*n;i++)a[i]=b[i]=read(),c[i]=i;
	sort(c+1,c+1+2*n,cmp);int cnt=2*n;
	for(int i=1;i<=2*n;i++)a[c[i]]=i;
	mi[0]=ni[n+1]=1e9+7;
	for(int i=1;i<=n;i++)mx[i]=max(a[i+n],mx[i-1]);
	for(int i=1;i<=n;i++)mi[i]=min(a[i+n],mi[i-1]);
	for(int i=n;i>=1;i--)nx[i]=max(a[i+n],nx[i+1]);
	for(int i=n;i>=1;i--)ni[i]=min(a[i+n],ni[i+1]);
	for(int i=1;i<=n;i++)pos[a[i]]=i;pos[cnt+1]=n+1;
	for(int i=cnt;i>=1;i--)
		if(!pos[i])pos[i]=pos[i+1];
	ans=1e9+7;int z=0;
	for(int i=1;i<=cnt;i++){
		while(z<i&&check(z+1,i))z++;
		if(z)ans=min(ans,b[c[i]]-b[c[z]]);
	}
	printf("%d
",ans);
}
原文地址:https://www.cnblogs.com/QuantAsk/p/14665944.html