ARC-138解题报告

比赛传送门

A. Larger Score

因为只需要增加而不限制增加量,所以找到一对前小后大的数对,设法将它们交换即可。具体来说,所以对于 \(k+1\)\(n\) 的一个位置 \(i\),找到在前 \(k\) 个当中离它最近的(最后的)、比它小的位置 \(j\)(取后缀 \(\min\) 后用 lower_bound 找出),用 \(k-j\) 次操作将 \(j\) 移到位置 \(k\),用 \(i-k-1\) 次操作将 \(i\) 移到位置 \(k+1\),再用一次操作交换即可,总操作数为 \((k-j)+(i-k-1)+1=i-j\) 次。这就是一个位置的答案。计算每个位置的答案,取最小值即可。

code:

#include<bits/stdc++.h>
using namespace std;
int a[400010];
int main() {
	int n,k,ans=1e9;
	cin>>n>>k;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=k-1;i>0;i--)
		a[i]=min(a[i],a[i+1]);
	for(int i=k+1;i<=n;i++) {
		int tmp=lower_bound(a+1,a+k+1,a[i])-a-1;
		if(tmp==0) continue;
		ans=min(ans,i-tmp);
	}
	if(ans==1e9) cout<<-1<<endl;
	else cout<<ans<<endl;
	return 0;
}

B. 01 Generation

由于只有前面加1反转和后面加0两种操作,所以考虑它们的分界线。

在前面的部分全部由添加1反转构成,所以一定是 010101... 这种形式,找出最长的满足要求的前缀,这就是第一部分。第一部分的0必须依次插入,可以发现,每插入一个0,第二部分就会受到影响,得到反转,所以当在第二部分中遇到连续的若干个0或连续的1的时候,它们一定(在最优解中)是同一块插入的。

而两块0、1的交界处必然进行了一次一操作(最优解中)。这样只需要比较操作一的次数(即最长01前缀的长度)和操作二的变化次数即可。如果操作一的次数小于第二部分的变化次数则不可行,反之可行。

这道题还有另一种思考方式,即倒序思考,将插入操作转化为删除。读者可自行考虑。

code:

#include<bits/stdc++.h>
using namespace std;
bool a[200010];
int main() {
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	if(a[1]==1) {
		cout<<"No"<<endl;
		return 0;
	}
	int now=1;
	while(now<n&&a[now]!=a[now+1]) now++;
	if(now==n) {
		cout<<"Yes"<<endl;
		return 0;
	}
	int ans=a[n];
	for(int i=n-1;i>now;i--)
		if(a[i]!=a[i+1]) ans++;
	if(ans<=now) cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	return 0;
}

C. Rotate and Play Game

题目问最好情况,那么有一个很自然的猜测:最好情况一定可以取得最优解,即拿走大的那一半,留下小的那一半。这就要求我们在翻转之后,在任何一个前缀当中,小的数的个数 \(\ge\) 大的数的个数(这样才能保证对方走到大的数之前,我已经把它拿掉了)。设较小的一半数为 \(+1\),较大的一半为 \(-1\),则相当于要求前缀和中不存在负数。在坐标系中表现出来就是这样的:

(数据 3 4 1 2 的图像)

那么,分成两半后互换就可以表示为,选一个点,以它为原点开始,再走回自己结束。这样结论就很显然了:选最低的点,以它为起点,坐标为0,那么之后无论怎么走也不会比它更低,满足要求。在此例子中,选择点 \(C\) 为起点,这样答案序列为 1 2 3 4,满足要求。

(不得不说是一道非常巧妙的转化题)

code:

#include<bits/stdc++.h>
using namespace std;
int a[200010],b[200010];
int main() {
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) {
		cin>>a[i];
		b[i]=a[i];
	}
	sort(b+1,b+n+1);
	int fen=b[n/2];
	long long mini=0,ans=0;
	for(int i=n/2+1;i<=n;i++)
		ans+=b[i];
	for(int i=1;i<=n;i++)
		if(a[i]<=fen) a[i]=1;
		else a[i]=-1;
	for(int i=2;i<=n;i++)
		a[i]+=a[i-1];
	for(int i=2;i<=n;i++)
		if(a[i]<a[mini])
			mini=i;
	cout<<mini<<" "<<ans<<endl;
	return 0;
}
posted @ 2023-03-01 16:07  曹轩鸣  阅读(17)  评论(0编辑  收藏  举报