我们可以直接对每个 $x$ 计算 $F_x(x)$,取最小值。($y$ 同理。)
计算 $F_x(x)$ 需要遍历所有 $i$,复杂度 $O(n)$,因此总复杂度 $O(n^2 + m^2)$,在 $n,m \le 1000$ 时炸不了。
为了避免浮点数,原式中含有 $\frac12$,我们可以把所有值乘以 $2$,最后再除回去。(为什么要避免浮点数呢,因为 ~~我就是不喜欢~~ 精度问题很难搞,可能会莫名其妙地变成“快乐的一只小青WA”。)
代码中的 `cal_x(x)` 计算的是:
$$
\sum_i r_i \cdot (2x - 2i - 1)^2
= 4 \sum_i r_i \left(x - i - \frac12\right)^2
= 4F_x(x)
$$
同理 `cal_y(y) = 4F_y(y)`。
所以总时间为:
$$
T = 16(F_x + F_y) = 4\bigl(4F_x + 4F_y\bigr) = 4\bigl(\text{cal\_x}(x) + \text{cal\_y}(y)\bigr)
$$
所以代码最后输出 `4 * (bvx + bvy)` 。
总时间复杂度 $O(nm + n^2 + m^2)$,空间复杂度 $O(n+m)$。
---
# AC Code
Tips:本人手欠用了`#define int long long`,据说不建议使用,请自行判断
::::success[CF201B Guess That Car!]
```cpp line-numbers
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1005;
int n, m;
int r[N], c[N];
int cal_x(int x) { // 计算当横坐标为 x 时,所有车横向距离平方贡献之和(不含系数4)
int res = 0;
for (int i = 0; i < n; i++) {
int d = 2LL * x - 2LL * i - 1; // 2*(x - i - 0.5)
res += r[i] * d * d;
}
return res;
}
int cal_y(int y) { // 计算当纵坐标为 y 时,所有车纵向距离平方贡献之和(不含系数4)
int res = 0;
for (int i = 0; i < m; i++) {
int d = 2LL * y - 2LL * i - 1; // 2*(y - i - 0.5)
res += c[i] * d * d;
}
return res;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int v;
cin >> v;
r[i] += v;
c[j] += v;
}
}
int bx = 0, bvx = LLONG_MAX;
for (int i = 0; i <= n; i++) {
int val = cal_x(i);
if (val < bvx) {
bvx = val;
bx = i;
}
}
int by = 0, bvy = LLONG_MAX;
for (int i = 0; i <= m; i++) {
int val = cal_y(i);
if (val < bvy) {
bvy = val;
by = i;
}
}
int ans = 4LL * (bvx + bvy);
cout << ans << '\n' << bx << ' ' << by;
return 0;
}
```
::::