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,j≡ai+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,j≡ai+bj①ci,k≡ai+bk②①−②⇒ci,j−ci,k≡bj−bk⇒bj−bj−1≡c1,j−c1,j−1≡c2,j−c2,j−1≡⋯⋯≡cn,j−cn,j−1
同理,
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}
ai−ai−1≡ci,1−ci−1,1≡ci,2−ci−1,2≡⋯⋯≡ci,m−ci−1,m
那么,我们就可以先把 a 和 b 的差分数组(对 MOD 取模)求出来。
接下来分析一下怎么求差分数组。
以 a 数组的为例,对于 a i − a i − 1 a_i-a_{i-1} ai−ai−1 我们可以得到很多个 c i , j − c i − 1 , j c_{i,j}-c_{i-1,j} ci,j−ci−1,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 A≡B 就不成立了,输出 NO 结束代码。
-
A
<
0
,
B
>
0
A<0\;,\;B>0
A<0,B>0,这时
A
≡
B
A\equiv B
A≡B 是有可能成立的,当且仅当
M
O
D
=
B
−
A
M\!O\!D=B-A
MOD=B−A,这样一来
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} ai−ai−1 任取 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,j−ci−1,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;
}