题解:CF201B Guess That Car!

· · 题解

(这题怎么没啥题解)

CF201B Guess That Car!

Content

停车场被划分为 n \times m 个小方格,每个小方格的边长为 4 米。每个小方格的正中心停着一辆车,价值为 C_{i,j}

你只能站在某个网格点(即网格线的交点)上猜车。网格点坐标用 (x,y) 表示,其中 x0ny0m

若你与某车的欧几里得距离为 d,则猜到这辆车需要花费 价值 × 的时间。

请你选择一个网格点,使得猜中所有车的总时间最小。若有多个最优点,优先输出 x 较小的,若 x 相同则输出 y 较小的。

Solution

1. 距离分解

设网格点坐标为 (x,y),第 i 行第 j 列的车(0 \le i < n,\;0 \le j < m)的中心位置为:

(4i + 2,\;4j + 2)

因为每格边长 4 米,中心在网格线偏移 2 米处。

那么欧几里得距离的平方为:

d^2 = \bigl(4x - (4i+2)\bigr)^2 + \bigl(4y - (4j+2)\bigr)^2 = 16\left[\left(x-i-\frac12\right)^2 + \left(y-j-\frac12\right)^2\right]

总花费:

T(x,y) = \sum_{i,j} C_{i,j} \cdot d^2 = 16\sum_{i,j} C_{i,j}\left(x-i-\frac12\right)^2 + 16\sum_{i,j} C_{i,j}\left(y-j-\frac12\right)^2

总花费可以拆成横向部分纵向部分之和,两部分相互独立。所以最优的 xy 可以分别求。

2. 压缩信息

因为横向部分只与每一行的总价值有关,纵向部分只与每一列的总价值有关。设:

r_i = \sum_{j=0}^{m-1} C_{i,j} \ \ ,\ \ \ \ c_j = \sum_{i=0}^{n-1} C_{i,j}

那么横向贡献为:

F_x(x) = \sum_{i=0}^{n-1} r_i \left(x - i - \frac12\right)^2

纵向贡献为:

F_y(y) = \sum_{j=0}^{m-1} c_j \left(y - j - \frac12\right)^2

总时间:

T(x,y) = 16\left(F_x(x) + F_y(y)\right)

3. 枚举

我们可以直接对每个 $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; } ``` ::::