专栏文章

题解:P11671 [USACO25JAN] Farmer John's Favorite Operation S

P11671题解参与者 3已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@miqaq5bb
此快照首次捕获于
2025/12/04 01:43
3 个月前
此快照最后确认于
2025/12/04 01:43
3 个月前
查看原文
来一发神奇二分做法。
我的整体思路是 O(n)O(n) 枚举 xx(经过证明,只有 nn 种情况),然后 O(logn)O(\log n) 求出每一种 xx 对应的答案。
首先将 aa 数组全部对 mm 取模,并排序。这样就得到了一个值域为 [0,m1][0,m-1] 的数组。容易发现,xx 一定等于某个 aia_i。这样,xx 的值域也确定为 [0,m1][0,m-1]。于是,aixa_i-x 的值域也确定了:[0(m1),(m1)0][0-(m-1),(m-1)-0][(m1),m1][-(m-1), m-1]
我们的目标是把 aia_i 变成 mm 的倍数,不妨看它的几何意义,即把数轴上移动到最靠近它的 mm 倍数点上。又由 aixa_i-x 的值域可知,最后移动到的 mm 倍数点只有三个:m-m00mm。这样,我们可以把减去 xx 之后,数组中的所有数字从小到大分为五类:
  1. m<aix<0-m < a_i-x < 0,且到 m-m 的距离更近,此时有 (aix)(m)<(aix)0|(a_i-x) - (-m)| < |(a_i-x) - 0|,解得 ai<2xm2a_i<\cfrac{2x-m}{2}
  2. m<aix<0-m < a_i-x < 0,且到 00 的距离更近,此时有 (aix)(m)(aix)0|(a_i-x) - (-m)| \geq |(a_i-x) - 0|,解得 ai2xm2a_i\geq \cfrac{2x-m}{2}
  3. aix=0a_i-x=0
  4. 0<aix<m0<a_i-x<m,且到 00 的距离更近,此时有 (aix)0<(aix)m|(a_i - x) - 0| < |(a_i - x) - m|,解得 ai<2x+m2a_i<\cfrac{2x+m}{2}
  5. 0<aix<m0<a_i-x<m,且到 mm 的距离更近,此时有 (aix)0(aix)m|(a_i - x) - 0| \geq |(a_i - x) - m|,解得 ai2x+m2a_i\geq \cfrac{2x+m}{2}
然后就可以通过 lower_boundupper_bound 确定这五类数的区间(由于数组 aa 单调递增,这五种数一定对应着数组上五个连续的区间,这五个区间无重叠、无遗漏地从左到右组成了数组 aa)。然后分别统计答案:
  1. 答案为 (aix)(m)=aix+m|(a_i-x)-(-m)|=a_i-x+m,三项拆贡献分别计算。
  2. 答案为 (aix)0=xai|(a_i-x)-0|=x-a_i,两项拆贡献分别计算。
  3. 答案为 00
  4. 答案为 (aix)0=aix|(a_i-x)-0|=a_i-x,两项拆贡献分别计算。
  5. 答案为 (aix)(m)=mai+x|(a_i-x)-(-m)|=m - a_i + x,三项拆贡献分别计算。
CPP
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll N = 2e5 + 10;
ll T, n, m, a[2 * N], s[2 * N];
inline ll calc(ll x) {
    return min(x % m, m - x % m);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> T;
    while (T--) {
        cin >> n >> m;
        for (int i = 1; i <= n; i ++) {
            cin >> a[i];
            a[i] = (a[i] % m);
        }
        sort(a + 1, a + n + 1);
        for (int i = 1; i <= n; i ++) {
            s[i] = s[i - 1] + a[i];
        }
        ll ans = 1e18;
        for (int i = 1; i <= n; i ++) {
            if (i != 1 && a[i] == a[i - 1]) continue;
            ll x = a[i], tmp = 0;
            ll idx = upper_bound(a + 1, a + n + 1
            , floor((2.0 * x - 1.0 * m) / 2.0)) - a - 1;
            if (idx > 0) {
                tmp += s[idx] - idx * x + idx * m;
            }
            if (idx + 1 <= i - 1) {
                tmp += abs(s[i - 1] - s[idx] - (i - 1 - idx - 1 + 1) * x);
            }
            ll idx2 = upper_bound(a + 1, a + n + 1, x) - a;
            ll idx3 = upper_bound(a + 1, a + n + 1
            , floor((2.0 * x + 1.0 * m) / 2.0)) - a - 1;
            if (idx2 <= idx3) {
                tmp += s[idx3] - s[idx2 - 1] - (idx3 - idx2 + 1) * x;
            }
            if (idx3 + 1 <= n) {
                tmp += m * (n - idx3) - (s[n] - s[idx3] - x * (n - idx3));
            }
            ans = min(ans, tmp);
        }
        cout << ans << "\n";
    }
    return 0;
}

评论

2 条评论,欢迎与作者交流。

正在加载评论...