ARC121D 1 or 2

有一个大小为 \(n\) 的可重集 \(S\),你需要将 \(S\) 划分为 \(k\) 个可重集,记第 \(i\) 个可重集为 \(S_i\),你需要保证 \(1 \leq |S_i| \leq 2\)。记作 \(f(S_i)\)\(S_i\) 中所有元素之和,你需要最小化 \(\max\limits_{i=1}^{k} f(S_i)-\min\limits_{i=1}^{k} f(S_i)\)

\(1 \leq n \leq 5 \times 10^3\)\(|a_i| \leq 10^9\)

脑电波题。

考虑枚举满足 \(|S_i|=1\) 的下标 \(i\) 的个数,记其为 \(k\)。我们考虑统一 \(|S_i|\),容易想到在 \(S\) 中加入 \(k\)\(0\),转化为两两配对。这样可能会出现两个 \(0\) 互相配对的情况,但此时容易发现我们删除所有配对的 \((0,0)\) 后仍然是合法的,这个方案一定会被统计,且加上若干对 \((0,0)\) 后答案一定不会更优。

接下来问题等价于将 \(2n\) 个数两两分组,每组两个数的和构成集合 \(T\),求 \(T\) 的极差最小值。我们给出结论:将 \(2n\) 个数排序,首尾配对即可。我们尝试证明这样做是最优的,不妨考虑四个数 \(a \leq b \leq c \leq d\),有三种情况:

  • \((a,b)\)\((c,d)\),得到的两个数为 \(a+b\)\(c+d\)

  • \((a,c)\)\((b,d)\),得到的两个数为 \(a+c\)\(b+d\)

  • \((a,d)\)\((b,c)\),得到的两个数为 \(a+d\)\(b+c\)

考虑大小关系,发现 \(a+b \leq a+c \leq a+d,b+c \leq b+d \leq c+d\),容易证明极差最小的情况一定是 \(a+d\)\(b+c\),对 \(n\) 个数进行若干次这样的调整后,最后的方案一定是首尾配对。

由于我是懒懒 Oken,所以写了 \(O(n^2 \log n)\) 的做法。

由于我不是懒懒 Oken,所以写了 \(O(n^2)\) 的做法。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long a[10010];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	} 
	sort(a+1,a+1+n);
	long long ans=0x3f3f3f3f3f3f3f3f;
	for(int i=0;i<=n;i++){
		if((n+i)%2==0){
			long long minn=0x3f3f3f3f3f3f3f3f,maxn=-0x3f3f3f3f3f3f3f3f;
			for(int l=1,r=n+i;l<r;l++,r--){
				minn=min(minn,a[l]+a[r]);
				maxn=max(maxn,a[l]+a[r]);
			}
			ans=min(ans,maxn-minn);
		}
		for(int j=n+i;j>=1;j--){
			if(a[j]>a[j+1]){
				swap(a[j],a[j+1]);
			}
		}
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2025-12-19 14:59  Oken喵~  阅读(6)  评论(0)    收藏  举报