[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 ;
}
本文来自 CaoSheng 的 Blogs,如需转载请标出原文链接:https://www.cnblogs.com/CaoSheng-blog/p/18700171

浙公网安备 33010602011771号