Simfonija 题解

Simfonija 题解

题目链接

题意

给定两个长度为 \(n\) 的数组 \(A\)\(B\),你可以给 \(A\) 数组中的所有元素加上 \(X\)(这里 \(X\) 应该能是负数),并修改不超过 \(K\) 个元素,使得下列代数式最小:

\[\sum_{i = 1}^{n} \lvert A_i - B_i \rvert \]

思路

首先我们可以考虑 \(K = 0\) 的情况。因为是让差最小,所以我们并不关心 \(A_i\)\(B_i\) 的值,只关心他们的差值即可。我们令 \(d_i\) 表示 \(B_i-A_i\)。如果没有 \(X\),那么最后答案可以分为三部分:\(d_i < 0\)\(d_i = 0\)\(d_i>0\)。这时候,如果我们将 \(d_i\) 排序,会发现其值呈阶梯状分布,如图:

pPQpWFI.png

我们发现,答案就是这些黑线覆盖的面积。(想象一下从每一条黑线两侧延伸下来两条边,形成若干个矩形)

现在我们来考虑 \(X\)。我们发现,给 \(A\) 加上 \(X\) 相当于在上下平移这条红线,即 \(0\) 所在的位置。我们来考虑平移时面积的变化 :

pPQCFKS.png
假如红线现在向上平移,设绿色面积为 \(s_1\),蓝色面积为 \(s_2\),两条黑线 \(d_1\)\(d_2\) 交界处为 \(mid\),我们发现,由于 \(mid\) 偏左,故向上平移时,左侧面积的增加量要小于右侧面积的减少量,所以向上平移答案会更优;同理,如果 \(mid\) 偏右,结果相反。不难想到,\(mid\),也就是 \(0\) 值的位置,应该位于一个靠中间的位置。而这个位置就是中位数所在的位置。

但是我们还有 \(K\) 个数可以改动。显然,每次改动一定是让某个 \(B_i\) 等于 \(A_i\),也就是让 \(d_i\)\(0\)。另一个显然的事实是,无论 \(mid\) 选在哪里,需要改动的 \(d_i\) 一定是左右两侧连续的几个。那么,我们可以枚举左侧选 \(i\) 个,那么右侧就是 \(K-i\) 个。每次的中位数是单调不减的。我们只需要处理好每次改变 \(0\) 值点后答案的变化即可。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
const int M = 2e6+100, del = 2000000; 
inline int read(){
	int x = 0, f = 1; char ch = getchar();
	while(ch<'0' || ch>'9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch>='0'&&ch<='9') {x = x*10+ch-48, ch = getchar();}
	return x * f;
}

int n, K;
int a[N], b[N], d[N];
int e[N];
int ed[N];
int id[M<<1], idx;
int main(){
// 	freopen("simfonija.in", "r", stdin);
// 	freopen("simfonija.out", "w", stdout);
	n = read(), K = read();
	for(int i = 1; i<=n; ++i){
		a[i] = read();
	}
	for(int i = 1; i<=n; ++i){
		b[i] = read();
	}
	for(int i = 1; i<=n; ++i){
		d[i] = b[i] - a[i];
	}
	sort(d+1, d+n+1);
	for(int i = 1; i<=n; ++i){
		if(i == n || d[i+1]!=d[i]){
			id[d[i]+del] = ++idx;
			ed[idx] = i;  
		}//预处理每段黑边的右端点。
	}
	int len = (n+1-K)/2;
	int X = d[len];
	long long ans = 0;
	for(int i = 1; i<=(n-K); ++i){
		ans+=abs(d[i]-X);
	}//左侧不选的答案
	int lst = X;
	long long ret = ans;
	for(int i = 1; i<=K; ++i){
		int nX = d[i+len];
		ans+= abs(d[n-K+i]-lst);//加上右侧弃选后增加的答案
		ans-= abs(d[i]-lst);//减去左侧选后减少的答案
		if(nX != lst){//如果中位数有变化,则 0 值线升高,要处理面积变化。
			ans-=(1ll*((n-K+i)-ed[id[lst+del]]) * (nX-lst));
			ans+=(1ll*(nX-lst)*(ed[id[lst+del]] - i));
			lst = nX;
		}
		ret = min(ret, ans);//答案取最小的
	}
	printf("%lld\n", ret);
	return 0;
}
posted @ 2023-08-16 00:03  霜木_Atomic  阅读(11)  评论(0编辑  收藏  举报