Solution to luogu P7514——普及选手也能看懂!

题目链接

洛谷

题目大意

一堆二元组,按第一个元素升序排列,现在可以使用 m m m次操作,每次操作使一个二元组两个元素交换,求所有二元组第一个元素构成的数组的极差的最小值。

考场解法

大法师, 20 p t s 20pts 20pts

考完之后发现我傻*了。

n 2 n^2 n2解法(普及组难度)

显然,产生极差的最大值和最小值一定都是某个二元组的元素(不然零头会被浪费)。也就是说最大值和最小值均只有 2 n 2n 2n级别种情况。

先把这 2 n 2n 2n个元素理起来排好序,再枚举最大值和最小值,判断是否合法即可。

考虑判断。枚举完最大值和最小值,就必定有一连串卡片( a i ∈ [ m i n , m a x ] a_i∈[min,max] ai[min,max])的不用翻了。其他的全要翻。那么就有两种情况使答案不合法。

1、要翻的卡片数量 > m >m >m。用 n n n减去不要翻的即可。

2、要翻的卡片中,有 b i b_i bi值在枚举范围之外的(这些卡片怎么翻也没用)。由于不要翻的卡片是连续一段(题中 a i a_i ai升序),要翻的卡片一定是前后缀,故预处理 b i b_i bi前后缀最值,若最值不在该范围内则整组解不合法。

复杂度 O ( 2 n × 2 n ) = O ( n 2 ) O(2n×2n)=O(n^2) O(2n×2n)=O(n2)。期望 40 p t s 40pts 40pts

线性解法(也是普及组难度)

考虑答案的单调性。

如果值域 [ x , y ] [x,y] [x,y]不可行,那么值域 [ x + k , y ] ( k > 0 ) [x+k,y](k>0) [x+k,y](k>0)一定不可行。证明分情况讨论。

  • 若上次是因为翻牌太多不可行,那值域变小意味着要翻的牌更多,显然不可行。

  • 若上次是因为有一张牌正反面均超出值域,那这一次值域变小,更加超出值域,不可行。

由于我们要做这道题,所以每一次的 y y y一定是在当前 x x x下最小的,也就是 [ x , y − 1 ] [x,y-1] [x,y1]就不行了,所以当 x x x变大时, y y y至少是上次的 y y y。也即, y y y x x x单调不减。

考虑 T w o _ P o i n t e r s Two\_Pointers Two_Pointers。复杂度 O ( n ) O(n) O(n)。期望 100 p t s 100pts 100pts。具体细节看代码。

附两组样例。

#include<bits/stdc++.h>
using namespace std;
typedef long long ak;
#define im INT_MAX
#define F(i,j,k) for(int i=j;i<=k;i++)
#define G(i,j,k) for(int i=j;i>=k;i--)
int ans=im,cur=1,a[1111111],b[1111111],c[2222222],n,m,mn=im,mx=-im,xj[2222222],sj[2222222],premin[1111111],premax[1111111],sufmin[1111111],sufmax[1111111];
bool lpc(int i,int j){
	return i<j;
}
bool cxk(int jcd){
	int l=xj[cur],r=sj[jcd];
	if(l>r)return 0;
	if(r==n&&l==1)return 1;
	if(r==n){
		if(l-1>m)return 0;
		if(premin[l-1]<c[cur])return 0;
		if(premax[l-1]>c[jcd])return 0;
		return 1;
	}
	if(l==1){
		if(n-r>m)return 0;
		if(sufmin[r+1]<c[cur])return 0;
		if(sufmax[r+1]>c[jcd])return 0;
		return 1;
	}
	if(n-r+l-1>m)return 0;
	if(premin[l-1]<c[cur])return 0;
	if(premax[l-1]>c[jcd])return 0;
	if(sufmin[r+1]<c[cur])return 0;
	if(sufmax[r+1]>c[jcd])return 0;
	return 1;
}
int main(){
	
	ios::sync_with_stdio(0);
	cin>>n>>m;
	F(i,1,n)cin>>a[i];
	F(i,1,n)cin>>b[i];
	F(i,1,n){
		mn=min(mn,b[i]);
		premin[i]=mn;
		mx=max(mx,b[i]);
		premax[i]=mx;
		c[i*2-1]=b[i];
		c[i<<1]=a[i];
	}
	mx=-im,mn=im;
	G(i,n,1){
		mn=min(mn,b[i]);
		sufmin[i]=mn;
		mx=max(mx,b[i]);
		sufmax[i]=mx;
	}
	sort(c+1,c+n*2+1,lpc);
	a[n+5]=im;//´Ë´¦·ÀÖ¹cur³¬½ç¡£ÎÒΪ´Ë±¬ÁËÒ»´ÎÁã¡£
	F(i,1,n<<1){
		while(a[cur]<c[i])cur++;
		xj[i]=cur;
	}
	cur=n;
	G(i,n<<1,1){
		while(a[cur]>c[i])cur--;
		sj[i]=cur;
	}
	int jcd=1;
	F(i,1,n<<1){
		cur=i;bool f=0;
		while(!cxk(jcd)){
			jcd++;
			if(jcd>n*2){
				f=1;break;
			}
		}
		if(f)break;
		ans=min(ans,c[jcd]-c[cur]);
	}
	cout<<ans<<"\n";
	return 0;
}
/*

10 5
 3  5  9 11 13 17 19 22 24 29
13 12 18  5  7 33 12 10 22 11
ans=11

10 5
180 193 194 225 260 273 276 314 316 322 
271 1 255 234 196 500 250 240 218 205 
ans=80

*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值