「解题报告」ARC135D Add to Square

这种题的第一想法应当是找不变量。

如果给原图黑白染色,那么每次操作都是操作两个黑格两个白格,那么黑格与白格的差不变。如果我们给黑格的数乘上 \(-1\),那么就是所有格子的和不变。

同时由于是 \(2 \times 2\),还能发现一行 / 一列的和不变。于是猜测当每一行的和与每一列的和都与原来相等时,该结果合法。

考虑将原来的与结果的 \([1, h - 1] [1, w - 1]\) 的所有格子全部消为 \(0\),这样每一行相等、每一列相等说明这几个数相等,说明可以从原局面得出。

那么相当于我们有 \(\{x_1, x_2, \cdots, x_h\}\)\(\{y_1, y_2, \cdots, y_w\}\),每次选择一个 \((i, j, d)\),使 \(x_i \gets x_i - d, y_j \gets y_j - d, b_{i, j} \gets b_{i, j} + d\),答案加 \(|d|\),最终要求让所有的 \(x, y\) 都变成 \(0\),使答案最小。

首先答案的一个下界为 \(\max \{\sum |x_i|, \sum |y_i|\}\)。我们可以构造出这个下界。

首先一直选两个都大于 \(0\) 或都小于 \(0\)\(x\)\(y\),将它们其中一个变为 \(0\)。由于 \(\sum x_i = \sum y_i\),说明现在肯定 \(x\) 或者 \(y\) 全部为 \(0\),接下来我们每次把不为 \(0\) 的一遍的一个大于 \(0\) 的和一个小于 \(0\) 的,其中一个变为 \(0\),这样一定可以使答案最终都变为 \(0\),而总花费也取到了下界。那么根据这个过程构造答案就行了。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 505;
int h, w;
long long a[MAXN][MAXN], x[MAXN], y[MAXN];
long long b[MAXN][MAXN];
int main() {
    scanf("%d%d", &h, &w);
    for (int i = 1; i <= h; i++) {
        for (int j = 1; j <= w; j++) {
            scanf("%lld", &a[i][j]);
            a[i][j] *= ((i + j) & 1) ? -1 : 1;
            x[i] += a[i][j];
            y[j] += a[i][j];
        }
    }
    long long ans = 0;
    while (true) {
        bool flag = false;
        for (int i = 1; i <= h; i++) if (x[i] > 0) {
            for (int j = 1; j <= w; j++) if (y[j] > 0) {
                long long delta = min(x[i], y[j]);
                x[i] -= delta, y[j] -= delta, ans += delta;
                b[i][j] += delta;
                flag = true;
            }
        }
        for (int i = 1; i <= h; i++) if (x[i] < 0) {
            for (int j = 1; j <= w; j++) if (y[j] < 0) {
                long long delta = min(-x[i], -y[j]);
                x[i] += delta, y[j] += delta, ans += delta;
                b[i][j] -= delta;
                flag = true;
            }
        }
        if (!flag) break;
    }
    while (true) {
        bool flag = false;
        for (int i = 1; i <= h; i++) if (x[i] > 0) {
            for (int j = 1; j <= h; j++) if (x[j] < 0) {
                long long delta = min(x[i], -x[j]);
                x[i] -= delta, x[j] += delta, ans += 2 * delta;
                b[i][1] += delta;
                b[j][1] -= delta;
                flag = true;
            }
        }
        for (int i = 1; i <= w; i++) if (y[i] > 0) {
            for (int j = 1; j <= w; j++) if (y[j] < 0) {
                long long delta = min(y[i], -y[j]);
                y[i] -= delta, y[j] += delta, ans += 2 * delta;
                b[1][i] += delta;
                b[1][j] -= delta;
                flag = true;
            }
        }
        if (!flag) break;
    }
    printf("%lld\n", ans);
    for (int i = 1; i <= h; i++) {
        for (int j = 1; j <= w; j++) {
            printf("%lld ", b[i][j] * (((i + j) & 1) ? -1 : 1));
        }
        printf("\n");
    }
    return 0;
}
posted @ 2023-01-30 11:01  APJifengc  阅读(61)  评论(0编辑  收藏  举报