一道构造题
Problem
给定一个\(n\times m\)的矩阵,每次可以对矩阵做以下操作:
1、给矩阵的一行同时加上一个数\(k\);
2、给矩阵的一列同时加上一个数\(k\);
3、给矩阵的一条对角线同时加上一个数\(k\)。给定\(t\),即\(\forall j-i=t\),满足的\(a_{i,j}\)即为一条对角线。
构造一种方案使得矩阵最后全为0。无解输出-1。
\(n,m\leq 10^3\)。
Solution
这是一道构造题,我们来考查操作下不变的关系。
考查一个\(3\times 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\n", 2*(n+m)-1);
for (int i = 1; i <= n; i++) printf("1 %d %lld\n", i, x[i]);
for (int i = 1; i <= m; i++) printf("2 %d %lld\n", i, y[i]);
for (int i = 1; i <= n+m-1; i++) printf("3 %d %lld\n", i-n, z[i]);
return 0;
}