「解题报告」ARC135D Add to Square

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

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

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

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

那么相当于我们有 {x1,x2,,xh}{y1,y2,,yw},每次选择一个 (i,j,d),使 xixid,yjyjd,bi,jbi,j+d,答案加 |d|,最终要求让所有的 x,y 都变成 0,使答案最小。

首先答案的一个下界为 max{|xi|,|yi|}。我们可以构造出这个下界。

首先一直选两个都大于 0 或都小于 0xy,将它们其中一个变为 0。由于 xi=yi,说明现在肯定 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 @   APJifengc  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示