最小差值
题目描述
给你一个由 $n$ 个整数组成的数组 $\{a_1,a_2,\dots,a_n\}$,以及一个由 $m$ 个整数组成的数组 $\{b_1,b_2,\dots,b_m\}$,现在我们需要寻找一个最小的整数 $k$,满足 $-2 \times 10^9 \leq k \leq 2 \times 10^9$,使得下式达到最小:$$\left \lvert \bigg(\sum\limits_{i=1}^{n}\left \lvert a_i - k \right \rvert\bigg) - \bigg(\sum\limits_{j=1}^{m}\left \lvert b_j - k \right \rvert\bigg) \right \rvert$$
直接输出满足条件的最小 $k$ 即可。
输入描述:
第一行输入两个整数 $n,m\left(1\leq n,m\leq 10^6\right)$ 代表数组 $a$ 中的元素个数、数组 $b$ 中的元素个数。
第二行输入 $n$ 个整数 $a_1,a_2,\dots,a_n\left(-10^9\leq a_i\leq 10^9\right)$。
第三行输入 $m$ 个整数 $b_1,b_2,\dots,b_m\left(-10^9\leq b_j\leq 10^9\right)$。
输出描述:
直接输出满足条件的最小 $k$ 即可。
示例1
输入
5 5
1 2 3 4 5
1 2 3 4 5
输出
-2000000000
说明
在这个样例中,我们会发现,$k$ 的取值不影响最后的答案,所以选择最小的 $k=-2\times 10^9$ 即可。
示例2
输入
5 3
1 5 9 3 6
2 5 10
输出
3
说明
在这个样例中,$k=3$ 时,有:$$\begin{aligned}\begin{aligned}\bigg(\sum\limits_{i=1}^{5}\left \lvert a_i - 3 \right \rvert\bigg) &= \left \lvert 1 - 3 \right \rvert + \left \lvert 5 - 3 \right \rvert + \left \lvert 9 - 3 \right \rvert + \left \lvert 3 - 3 \right \rvert + \left \lvert 6 - 3 \right \rvert \\&= 2 + 2 + 6 + 0 + 3 \\&= 13\end{aligned}\\\begin{aligned}\bigg(\sum\limits_{j=1}^{3}\left \lvert b_j - 3 \right \rvert\bigg) &= \left \lvert 2 - 3 \right \rvert + \left \lvert 5 - 3 \right \rvert + \left \lvert 10 - 3 \right \rvert \\&= 1 + 2 + 7 \\&= 10\end{aligned}\end{aligned}$$
综上,式子的答案为 $\left \lvert 13 - 10 \right \rvert = 3$,我们可以证明,无法再找到一个 $-2\times 10^9\leq k\leq 3$,使得式子的值 $\le 3$。
解题思路
由于排序不影响结果,为了方便先对数组 $a$ 和 $b$ 从小到大排序。我们可以枚举每一个 $k$ 来求式子的最小值,但细想一下会发现,当 $k$ 落在某个范围内时,式子的结果是不变的。具体来说,我们把数组 $a$ 和 $b$ 进行合并同时从小到大排序,得到数组 $p$。对于任意 $k \in [p_i, p_{i+1}]$ 时,此时 $k$ 必然是 $a$ 的两个相邻元素之间的某个值,同时也是 $b$ 的两个相邻元素之间的某个值,此时 $\bigg(\sum\limits_{i=1}^{n}\left \lvert a_i - k \right \rvert\bigg)$ 和 $\bigg(\sum\limits_{j=1}^{m}\left \lvert b_j - k \right \rvert\bigg)$ 的结果都相同,因此式子不变。
这启发我们只需关注每一段 $[p_i, p_{i+1}], \, (1 \leq i < n+m)$ 即可。假设当前枚举到第 $i$ 段 $[p_i, p_{i+1}]$,为了描述把式子中的 $k$ 换成 $x$,此时 $x \, (p_i \leq x \leq p_{i+1})$ 应该取值多少,才能使得式子有最小值?假设 $a$ 中有 $c_a$ 个元素小于等于 $x$(意味着 $n - c_a$ 个元素大于 $x$),$b$ 中有 $c_b$ 个元素小于等于 $x$(意味着 $m - c_b$ 个元素大于 $x$)。此时式子的结果等于
\begin{align*}
&\left \lvert \bigg(\sum\limits_{i=1}^{n}\left \lvert a_i - x \right \rvert\bigg) - \bigg(\sum\limits_{j=1}^{m}\left \lvert b_j - x \right \rvert\bigg) \right \rvert \\
= &\left \lvert \sum\limits_{i=1}^{c_a}{x-a_i} + \sum\limits_{i=c_a+1}^{n}{a_i-x} - \left( \sum\limits_{i=1}^{c_b}{x-b_i} + \sum\limits_{i=c_b+1}^{m}{b_i-x} \right) \right \rvert \\
= &\left \lvert c_ax - \sum\limits_{i=1}^{c_a}{a_i} + \sum\limits_{i=c_a+1}^{n}{a_i} - (n-c_a)x - \left( c_bx - \sum\limits_{i=1}^{c_b}{b_i} + \sum\limits_{i=c_b+1}^{m}{b_i} - (m-c_b)x \right) \right \rvert \\
= &\left \lvert \left(c_a - (n - ca) + (m - c_b) - c_b\right)x + \left(- \sum\limits_{i=1}^{c_a}{a_i} + \sum\limits_{i=c_a+1}^{n}{a_i}- \sum\limits_{i=1}^{c_b}{b_i} + \sum\limits_{i=c_b+1}^{m}{b_i} \right) \right \rvert \\
\end{align*}
记 $k = \left(c_a - (n - ca) + (m - c_b) - c_b\right)$,$b = \left(- \sum\limits_{i=1}^{c_a}{a_i} + \sum\limits_{i=c_a+1}^{n}{a_i}- \sum\limits_{i=1}^{c_b}{b_i} + \sum\limits_{i=c_b+1}^{m}{b_i} \right)$,因此就是求折线 $|kx+b|$ 的最小值。
只需分情况讨论即可,首先是两个端点 $p_i$ 和 $p_{i+1}$。然后是可能的最小点 $kx+b = 0 \Rightarrow x = \frac{-b}{k}$,由于 $x$ 是整数,因此需要考虑以下三个取值,$\left \lfloor \frac{-b}{k} \right \rfloor-1$、$\left \lfloor \frac{-b}{k} \right \rfloor$、$\left \lfloor \frac{-b}{k} \right \rfloor +1$(同时需要保证 $p_i \leq x \leq p_{i+1}$)。
AC 代码如下,时间复杂度为 $O((n+m)\log{(n+m)})$:
#include <bits/stdc++.h>
using namespace std;typedef long long LL;const int N = 1e6 + 5, M = N * 2;int a[N], b[N];
LL sa[N], sb[N];
array<int, 2> p[M];int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, m;cin >> n >> m;for (int i = 1; i <= n; i++) {cin >> a[i];}for (int i = 1; i <= m; i++) {cin >> b[i];}sort(a + 1, a + n + 1);sort(b + 1, b + m + 1);for (int i = 1; i <= n; i++) {sa[i] = sa[i - 1] + a[i];}for (int i = 1; i <= m; i++) {sb[i] = sb[i - 1] + b[i];}int sz = 0;for (int i = 1; i <= n; i++) {p[sz++] = {a[i], 0};}for (int i = 1; i <= m; i++) {p[sz++] = {b[i], 1};}p[sz++] = {int(-2e9), -1};p[sz++] = {int(2e9), -1};sort(p, p + sz);LL mn = 1e18, ret;for (int i = 0, ca = 0, cb = 0; i + 1 < sz; i++) {if (!p[i][1]) ca++;else if (p[i][1] == 1) cb++;LL k = ca - (n - ca) + (m - cb) - cb, b = sa[n] - 2 * sa[ca] + 2 * sb[cb] - sb[m];auto get = [&](LL x) {if (x < p[i][0] || x > p[i + 1][0]) return;if (abs(k * x + b) < mn) {mn = abs(k * x + b);ret = x;}};get(p[i][0]);if (k) {get(-b / k - 1);get(-b / k);get(-b / k + 1);}get(p[i + 1][0]);}cout << ret;return 0;
}
参考资料
小白月赛113题解:https://ac.nowcoder.com/discuss/1480037