LYK loves matrix (构造,同余)

题面

LYK 有一个 1×n 的非负整数数列 a 与 1×m 的非负整数数列 b。它打算将它们合并成一个 matrix。它合并的方式是这样的:
c i , j = ( a i + b j )   %   M  ⁣ O  ⁣ D c_{i,j}=(a_i+b_j)\,\%\,M\!O\!D ci,j=(ai+bj)%MOD

那么 c 就成了一个 n×m 的 matrix。 然而 LYK 惊奇地发现当它构造完这个矩阵后,它忘了 a、b 与 MOD 具体的值。 LYK 打算告诉你矩阵 c,想让你还原出一开始的 a、b 与 MOD。如果可行,第一行输出 “YES”,并在接下来几行中输出 MOD,a 与 b 具体的值(输出任意一种合法解)。当然 LYK 可能故意坑你告诉了你错误的信息,此时你应当输出“NO”。

对于 10%的数据,LYK 在坑你。😃
对于另外 30%的数据 c i , j = a i + b j c_{i,j}=a_i+b_j ci,j=ai+bj
对于另外 30%的数据 n,m<=7。
对于 100%的数据 n,m<=100, c i , j c_{i,j} ci,j <= 1 0 9 10^9 109

题解

从这个核心式子入手:
c i , j ≡ a i + b j      (  ⁣ ⁣ ⁣ ⁣ m o d    M  ⁣ O  ⁣ D ) c_{i,j}\equiv a_i+b_j\;\;(\!\!\!\!\mod M\!O\!D) ci,jai+bj(modMOD)

我们从 c 中元素之间的关系推导一下:
c i , j ≡ a i + b j        ① c i , k ≡ a i + b k        ② ① − ② ⇒ c i , j − c i , k ≡ b j − b k ⇒    b j − b j − 1 ≡ c 1 , j − c 1 , j − 1 ≡ c 2 , j − c 2 , j − 1 ≡ ⋯ ⋯ ≡ c n , j − c n , j − 1 \begin{matrix} c_{i,j}\equiv a_i+b_j\;\;\;①\\ c_{i,k}\equiv a_i+b_k\;\;\;②\\ ①-② \Rightarrow c_{i,j}-c_{i,k}\equiv b_j-b_k\\ \Rightarrow\;b_j-b_{j-1}\equiv c_{1,j}-c_{1,j-1}\equiv c_{2,j}-c_{2,j-1}\\ \equiv \cdots\cdots \equiv c_{n,j}-c_{n,j-1} \end{matrix} ci,jai+bjci,kai+bkci,jci,kbjbkbjbj1c1,jc1,j1c2,jc2,j1cn,jcn,j1

同理,
a i − a i − 1 ≡ c i , 1 − c i − 1 , 1 ≡ c i , 2 − c i − 1 , 2 ≡ ⋯ ⋯ ≡ c i , m − c i − 1 , m a_i-a_{i-1}\equiv c_{i,1}-c_{i-1,1}\equiv c_{i,2}-c_{i-1,2} \equiv \cdots\cdots \equiv c_{i,m}-c_{i-1,m} aiai1ci,1ci1,1ci,2ci1,2ci,mci1,m

那么,我们就可以先把 a 和 b 的差分数组(对 MOD 取模)求出来。

接下来分析一下怎么求差分数组。

以 a 数组的为例,对于 a i − a i − 1 a_i-a_{i-1} aiai1 我们可以得到很多个 c i , j − c i − 1 , j c_{i,j}-c_{i-1,j} ci,jci1,j ,而它们很可能是不同的,对于其中一对不同的 A、B(不妨设 A < B),有以下几种情况:

  • 0 < A < B    o r    A < B < 0 0<A<B\;or\;A<B<0 0<A<BorA<B<0,由于 c 是取模后的,A 和 B 的绝对值肯定会小于 MOD,如果此时再同号的话,肯定 A ≡ B A\equiv B AB 就不成立了,输出 NO 结束代码。
  • A < 0    ,    B > 0 A<0\;,\;B>0 A<0,B>0,这时 A ≡ B A\equiv B AB 是有可能成立的,当且仅当 M  ⁣ O  ⁣ D = B − A M\!O\!D=B-A MOD=BA,这样一来 M  ⁣ O  ⁣ D > m a x ( ∣ A ∣ , ∣ B ∣ )    ,    A + M  ⁣ O  ⁣ D = B M\!O\!D>max(|A|,|B|)\;,\;A+M\!O\!D=B MOD>max(A,B),A+MOD=B,符合条件。MOD 如果大于 B-A 则肯定不成立,MOD 如果小于 B-A ,那么最大为 (B-A)/2,小于等于 A 和 B 的绝对值,也不符合条件。如果先前已经通过这一条求出一个 MOD,且与这一次求的 MOD 不一样,那么无解,输出 NO 。
    出现这种情况后,MOD 就确定了!此时 a i − a i − 1 a_i-a_{i-1} aiai1 任取 A 或 B 都没问题,反正都同余了。
  • A = 0 < B    o r    A < B = 0 A=0<B\;or\;A<B=0 A=0<BorA<B=0,此时一个是 MOD 的倍数,一个不是,是不可能同余的,输出 NO。

那么求完差分数组后还有解的话,要么 c 极其和谐,跟没取模似的,每个 c i , j − c i − 1 , j c_{i,j}-c_{i-1,j} ci,jci1,j 都一样,MOD 未知,此时相当于 MOD 是多少都不会影响结果了,令 MOD 等于 c 中最大的数 +1;要么出现过一个正一个负的情况,此时 MOD 已知。

可以证明,现在就一定有解了,因为 c 矩阵元素彼此之间的关系都处理出来了,只要知道 a 1 a_1 a1 b 1 b_1 b1 ,保证其和为 c 1 , 1 c_{1,1} c1,1 ,那么就可以通过差分数组把所有元素都求出来,不会出现矛盾。

因此不妨令 a 1 = 0    ,    b 1 = c 1 , 1 a_1=0\;,\;b_1=c_{1,1} a1=0,b1=c1,1 然后先把差分数组根据模数变成非负数,再构造出 a 和 b 就完了。

所以我们又证明了只要有解,就一定能构造出一个解满足 a 1 = 0 a_1=0 a1=0 b 1 = 0 b_1=0 b1=0

CODE

#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 105
#define LL long long
#define ULL unsigned long long
#define DB double
#define ENDL putchar('\n')
#define eps 1e-5
LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s == '-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
	return f * x;
}
int n,m,i,j,s,o,k,mod;
int Abs(int x) {return x < 0 ? -x:x;}
int c[MAXN][MAXN];
int da[MAXN],db[MAXN];
LL a[MAXN],b[MAXN];
int main() {
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	mod = -1; // 表示未知
	n = read();
	m = read();
	int maxc = 0;
	for(int i = 1;i <= n;i ++) {
		for(int j = 1;j <= m;j ++) {
			c[i][j] = read();
			maxc = max(maxc,c[i][j]);
			if(i > 1) {
				if(j == 1) da[i] = c[i][j] - c[i-1][j];
				else {
					int dt = c[i][j] - c[i-1][j];
					if(dt != da[i]) {
						if(dt *1ll* da[i] < 0) {
							if(mod == -1 || mod == Abs(dt-da[i])) mod = Abs(dt-da[i]);
							else {
								printf("NO\n");return 0;
							}
						}
						else {
							printf("NO\n");return 0;
						}
					}
				}
			}
		}
	}
	for(int j = 1;j <= m;j ++) {
		for(int i = 1;i <= n;i ++) {
			if(j > 1) {
				if(i == 1) db[j] = c[i][j] - c[i][j-1];
				else {
					int dt = c[i][j] - c[i][j-1];
					if(dt != db[j]) {
						if(dt *1ll* db[j] < 0) {
							if(mod == -1 || mod == Abs(dt-db[j])) mod = Abs(dt-db[j]);
							else {
								printf("NO\n");return 0;
							}
						}
						else {
							printf("NO\n");return 0;
						}
					}
				}
			}
		}
	}
	if(mod != -1 && mod <= maxc) {
		printf("NO\n"); return 0;
	}
	if(mod == -1) mod = maxc + 1;
	a[1] = 0; b[1] = c[1][1];
	for(int i = 2;i <= n;i ++) {
		a[i] = a[i-1] + (da[i]%mod+mod)%mod;
	}
	for(int i = 2;i <= m;i ++) {
		b[i] = b[i-1] + (db[i]%mod+mod)%mod;
	}
	for(int i = 1;i <= n;i ++) { // 傻傻地验证一下
		for(int j = 1;j <= m;j ++) {
			if((a[i]+b[j]) % mod != c[i][j]) {
				printf("NO\n"); return 0;
			}
		}
	}
	printf("YES\n%d\n",mod);
	for(int i = 1;i <= n;i ++) {
		printf("%lld ",a[i]);
	}ENDL;
	for(int i = 1;i <= m;i ++) {
		printf("%lld ",b[i]);
	}ENDL;
	return 0;
}
posted @ 2021-03-06 15:34  DD_XYX  阅读(7)  评论(0编辑  收藏  举报