ARC163B题解

2023/7/82023/7/8:把代码块的语言改成了 c++

题意简述

给一个长度为 NN 的序列 AA,你可以进行若干次操作:

  • 选择一个整数 ii 满足 1iN1\le i \le N,将 AiA_i 加上或减去 11

求最少需要多少步,使得至少有 MM 个整数 ii 满足 3iN3\le i \le NA1AiA2A_1 \le A_i \le A_2

解法

可以发现一个性质,只改变 A1,A2A_1,A_2 的值是最优的

证明

A1A_1 的值为 uu,如果它要包含的区间的最小值为 uu'(这里,u<uu'\lt u,因为如果 uuu'\ge uA1A_1 可以不做改变),则改变 A1A_1 只需 uuu-u' 步。

而如果改变的不是 A1A_1,仅仅是将 uu' 改成大于等于 uu 的数就要花 uuu-u' 步了,如果还有其它点的值 rr 满足 ur<uu'\le r\lt u,那步数一定大于 uuu-u' 步。

改变 A2A_2 同理。

综上可得,只改变 A1,A2A_1,A_2 的值是最优的。

再配一张图理解一下(其中,红色的点分别表示 A1,A2A_1,A_2,红线表示 A1,A2A_1,A_2 移动的步数,蓝线为其它点移动的步数,可以看出红线总长比蓝线总长短)

还有一个很好证明性质,包含的区间一定是按顺序(从大到小、从小到大都可以)排序中一个连续的子序列。

算法简述

对后 N2N-2 个点从小到大排序(当然,从大到小也可以),依次枚举长度为 MM 的区间(即左端点在 [3,NM+1][3,N-M+1] 的长度为 MM 的区间)。求出将 A1A_1 改变成小于等于左端点的值,A2A_2 改变成大于等于右端点的值最少需要的步数即可。

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,a1,a2,b[200010],ans=0x7fffffff;
bool cmp(int x,int y){
	return x<y;
}
int main(){
	scanf("%d%d",&n,&m);
	scanf("%d%d",&a1,&a2);//A1,A2分开读
	n-=2;
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	sort(b+1,b+n+1,cmp);
	for(int i=1;i<=n-m+1;i++) ans=min(ans,max(a1-b[i],0)+max(b[i+m-1]-a2,0));
	printf("%d",ans);
	return 0;
}
posted @ 2023-07-04 10:24  fengxiaoyi  阅读(47)  评论(0)    收藏  举报  来源