CF201B Guess That Car! 题解

CF201B Guess That Car!

Content

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

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

若你与某车的欧几里得距离为 \(d\),则猜到这辆车需要花费 价值 × \(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 \]

总花费可以拆成横向部分与纵向部分之和,两部分相互独立。所以最优的 \(x\) 和 \(y\) 可以分别求。


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\) 的取值范围只有 \(0,1,\dots,n\),共 \(n+1\) 个,\(y\) 同理。

我们可以直接对每个 \(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,据说不建议使用,请自行判断

#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;
}
posted @ 2026-08-11 18:43  Void__114  阅读(17)  评论(0)    收藏  举报