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

思路

因为 \(a_i - x = k \times m\) 所以我们可以列出 \(x \equiv \forall a_i (\bmod m)\)

我们称满足对 \(m\) 取模之后余数为 \(x\) 的数为除余数。

我们设 \(g(i, x)\)\(a_i\)​ 变成除余数的最小权值。

假设 \(a_i\) 前面一个除余数为 \(f\) 那么当 \(f + \lfloor \frac{m}{2} \rfloor \geq a_i\),这时将 \(a_i\) 变成 \(f\) 就为最优解,否则将 \(a_i\) 变成 \(f + m\) 才为最优解。

  • 提示:取模之后算和取模之前算的权值是一样的因为 \(f(\bmod m) - a_i (\bmod m) \equiv f - a_i\),所以我们在输入的时候就可以直接将 \(a_i\)\(m\) 取模。

不难推出最终答案就为 \(\sum \limits_{i=1}^{n} g(i,x)\)

通过上述公式不难发现一定有一个最优解 \(x = a_i\) 但是我们不确定 \(i\) 所以需要暴力循环。

但是这时候我们的时间复杂度就为 \(\Omicron(n^2)\),通过不了此题。

这时就有人问了主播主播这个做法时间复杂度还是太高了,有没有什么简单又快速的方法推荐一下

有的有的,我们无法优化找 \(x\) 的部分,所以我们直接考虑如何优化公式。因为公式中的 \(x\) 我们是确定的,所以我们可以将 \(a\) 数组分成两部分一部分满足 \(x \leq a_i \leq x + \lfloor \frac{m}{2} \rfloor\),另一部分满足 \(x - \lfloor \frac{m}{2} \rfloor \leq a_i < x\)。那么对于前者我们只需要将它们减小至 \(x\),对于后者我们只需要将他们增加至 \(x\)。因为前文我们我们将 \(a_i\) 取模过了,所以可以保证 \(x - \lfloor \frac{m}{2} \rfloor \leq \forall a_i \leq x + \lfloor \frac{m}{2} \rfloor\)

假设第一个大于等于 \(x - \lfloor \frac{m}{2} \rfloor\) 的位置在 \(l\),第一个大于等于 \(x\) 的位置在 \(mid\),最后一个数的位置在 \(r\) 那么我们可以推出公式 \(\sum \limits_{i=mid}^{r} (a_i - x) + \sum \limits_{i=l}^{mid-1} (x - a_i)\),这个时间复杂度还是 \(\Omicron(n)\) 但是我们可以很轻松的化简了,用 \(sum\) 数组记录 \(a\) 数组的前缀和。

那么公式就变成了 \((sum_r - sum_{mid - 1}) - (r - mid + 1) \times x + (mid - l) \times x - (sum_{mid - 1} - sum_l)\)

但是这里有一个细节就是 \(x + \lfloor \frac{m}{2} \rfloor\) 可能会大于 \(m\),或者 \(x - \lfloor \frac{m}{2} \rfloor\) 会小于 \(0\),我们只需要通过加减 \(m\) 维护边界就行了。

Code
const int maxn = 2e5 + 1 ;
int a[maxn], n, mod ;
ll sum[maxn] ;
ll ans ;
 
ll work(int pos) {
    int mid = mod >> 1 ; ll res = 0 ;
    int x1 = std::lower_bound(a + 1, a + n + 1, pos) - a ;
    int x2 = std::upper_bound(a + 1, a + n + 1, (pos + mid) % mod) - a - 1 ;
    if(x2 >= x1) {
        res += sum[x2] - sum[x1 - 1] - 1ll * (x2 - x1 + 1) * pos ;
        res += 1ll * (n - x2) * (pos + mod) - (sum[n] - sum[x2]) + 1ll * (x1 - 1) * pos - sum[x1 - 1] ;
    }
    else {
        res += 1ll * pos * (x1 - x2 - 1) - (sum[x1 - 1] - sum[x2]) ;
        res += sum[n] - sum[x1 - 1] - 1ll * (n - x1 + 1) * pos + sum[x2] + 1ll * x2 * mod - 1ll * x2 * pos ;
    } 
    return res ;
}
 
void Solve() {
    n = read(), mod = read(), ans = 1e15 ;
    for(int i=1 ; i<=n ; i++) a[i] = read() % mod ;
    std::sort(a + 1, a + n + 1) ;
    for(int i=1 ; i<=n ; i++) sum[i] = sum[i - 1] + a[i] ;
    for(int i=1 ; i<=n ; i++) ans = min(ans, work(a[i])) ;
    output(ans), ent ;
}
 
signed main() {
    int _T = read() ;
    while(_T --) Solve() ;
    return 0 ;
}
posted @ 2025-02-05 21:19  CaoSheng  阅读(77)  评论(0)    收藏  举报