一道构造题

Problem

给定一个(n imes m)的矩阵,每次可以对矩阵做以下操作:

1、给矩阵的一行同时加上一个数(k)

2、给矩阵的一列同时加上一个数(k)

3、给矩阵的一条对角线同时加上一个数(k)。给定(t),即(forall j-i=t),满足的(a_{i,j})即为一条对角线。

构造一种方案使得矩阵最后全为0。无解输出-1。

(n,mleq 10^3)

Solution

这是一道构造题,我们来考查操作下不变的关系。

考查一个(3 imes 3)的矩阵,可以发现:(a_{1,1}-a_{2,1}-a_{1,2}+a_{2,3}+a_{3,2}-a_{3,3})始终不变!

在三类操作下,变化的值总是能被抵消掉!(将其黑白染色)

所以只要能填好前两行和前两列,依据以上等式可以将全部矩阵推导出来。

问题就变成:将前两行前两列通过三种操作变成0,判断剩下位置是否为0。如果为0,则肯定无解;反之则有解。

那么现在如何将前两行前两列变成0?这个很简单,把(a_{1,1})(a_{2,2})变成0后,对于第一行和第一列的数字,利用操作三变成0;对于第二行第二列,利用一二操作,这样可以不影响到(a_{1,1})(a_{1,2})

#include <bits/stdc++.h>
typedef long long LL;
const int N = 1005;
int n, m, a[N][N];
LL x[N], y[N], z[N*2];
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			scanf("%d", &a[i][j]);
	x[1] = a[2][2]-a[1][1]; z[n] = -a[2][2];
	for (int i = 2; i <= n; i++) z[n-i+1] = -(a[i][1]+x[i]), x[i+1] = -(a[i+1][2]+z[n-i+1]);
	for (int i = 2; i <= m; i++) z[n+i-1] = -(a[1][i]+x[1]+y[i]), y[i+1] = -(a[2][i+1]+z[n+i-1]);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			if (a[i][j]+x[i]+y[j]+z[j-i+n]) { puts("-1"); return 0; }
	printf("%d
", 2*(n+m)-1);
	for (int i = 1; i <= n; i++) printf("1 %d %lld
", i, x[i]);
	for (int i = 1; i <= m; i++) printf("2 %d %lld
", i, y[i]);
	for (int i = 1; i <= n+m-1; i++) printf("3 %d %lld
", i-n, z[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/ac-evil/p/14015394.html