ARC135D

构造 \(X_i=\sum_{j=1}^m (-1)^{i+j}A_{i,j}\)\(Y_j=\sum_{i=1}^n (-1)^{i+j}A_{i,j}\)

则一个矩阵 \(B\) 能被矩阵 \(A\) 变成当且仅当 \(\left\{X\right\},\left\{Y\right\}\) 均相等。

必要性:很显然,因为操作是不会导致 \(\left\{X\right\}\)\(\left\{Y\right\}\) 改变的。

充分性:对于 \(i\in [1,n-1],j\in[1,m-1]\),从上往下,从左到右依次调整,而每行的最右端与每列的最下端因为 \(\left\{X\right\},\left\{Y\right\}\) 均相等被唯一确定。

转换问题,变成给定 \(\left\{X\right\},\left\{Y\right\}\),满足 \(\sum X_i=\sum Y_j\),求一个 \(B\) 满足 \(X_i=\sum_{j=1}^m (-1)^{i+j}B_{i,j}\)\(Y_j=\sum_{i=1}^n (-1)^{i+j}B_{i,j}\),最小化 \(\sum \left|B_{i,j}\right|\)

\(B\) 从全零矩阵开始操作,题目变成:

  • 每次操作 \((i,j,k)\),使得 \(X_i,Y_j\) 加上 \(k\),代价为 \(\left|k\right|\),需要让 \(\left\{X\right\},\left\{Y\right\}\) 都变成 \(0\),最小化代价。

显然有个下界是 \(\sum \left|B_{i,j}\right|\ge \max(\sum\left|X_i\right|,\sum\left|Y_j\right|)\),接下来构造一组能取到下界的解。

  • 如果存在 \(X_i,Y_j\gt0\),则操作 \((i,j,-1)\)
  • 如果存在 \(X_i,Y_j\lt0\),则操作 \((i,j,1)\)
  • 如果存在 \(X_i\gt0,X_j\lt0\),则操作 \((i,1,-1),(j,1,1)\)
  • 如果存在 \(Y_i\gt0,Y_j\lt0\),则操作 \((1,i,-1),(1,j,1)\)

容易发现由于 \(\sum X_i=\sum Y_j\),则前两种操作完后,必有 \(\left\{X\right\}\)\(\left\{Y\right\}\) 全为 \(0\),后面两种操作就是对剩下非 \(0\) 的进行处理,每一步都是必要且不浪费的,因此这样能取到下界。

时间复杂度 \(\mathcal O(n^2)\)

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 505;
int n, m;
ll ans;
ll b[N][N];
ll X[N], Y[N];

void upd(int x, int y, ll c) {
	X[x] -= c, Y[y] -= c;
	b[x][y] += ((x + y) & 1) ? -c : c;
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j) {
			ll k; scanf("%lld", &k);
			if ((i + j) & 1) X[i] -= k, Y[j] -= k;
			else X[i] += k, Y[j] += k;
		}
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			if (X[i] > 0 && Y[j] > 0)
				upd(i, j, min(X[i], Y[j]));
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			if (X[i] < 0 && Y[j] < 0)
				upd(i, j, max(X[i], Y[j]));
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			if (X[i] > 0 && X[j] < 0) {
				ll tmp = min(X[i], -X[j]);
				upd(i, 1, tmp);
				upd(j, 1, -tmp);
			}
	for (int i = 1; i <= m; ++i)
		for (int j = 1; j <= m; ++j)
			if (Y[i] > 0 && Y[j] < 0) {
				ll tmp = min(Y[i], -Y[j]);
				upd(1, i, tmp);
				upd(1, j, -tmp);
			}
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			ans += abs(b[i][j]);
	printf("%lld\n", ans);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) printf("%lld ", b[i][j]);
		printf("\n");
	}
	return 0;
}
posted @ 2022-11-10 14:13  Kobe303  阅读(23)  评论(0编辑  收藏  举报