题目
思路
显然我们只需要确定
a
i
,
1
(
i
≥
1
)
a_{i,1}\;(i\ge 1)
ai,1(i≥1) 和
a
1
,
j
(
j
>
1
)
a_{1,j}\;(j>1)
a1,j(j>1) 的值,即可还原整个矩阵。经过简单的推导可以发现
a
i
,
j
=
v
i
,
j
+
(
−
1
)
j
−
1
a
i
,
1
+
(
−
1
)
i
−
1
a
1
,
j
+
(
−
1
)
i
+
j
−
1
a
1
,
1
a_{i,j}=v_{i,j}+(-1)^{j-1}a_{i,1}+(-1)^{i-1}a_{1,j}+(-1)^{i+j-1}a_{1,1}
ai,j=vi,j+(−1)j−1ai,1+(−1)i−1a1,j+(−1)i+j−1a1,1
其中
v
i
,
j
=
b
i
−
1
,
j
−
1
−
v
i
−
1
,
j
−
v
j
,
i
−
1
−
v
i
−
1
,
j
−
1
v_{i,j}=b_{i-1,j-1}-v_{i-1,j}-v_{j,i-1}-v_{i-1,j-1}
vi,j=bi−1,j−1−vi−1,j−vj,i−1−vi−1,j−1,这是由
b
i
,
j
b_{i,j}
bi,j 的定义可得的。
发现
a
i
,
1
a_{i,1}
ai,1 的系数竟然由
j
j
j 决定,这不利于我们发现一些内在的规律。考虑两边同时乘
(
−
1
)
i
+
j
(-1)^{i+j}
(−1)i+j 得
(
−
1
)
i
+
j
a
i
,
j
=
(
−
1
)
i
+
j
v
+
(
−
1
)
i
−
1
a
i
,
1
+
(
−
1
)
j
−
1
a
1
,
j
−
a
1
,
1
(-1)^{i+j}a_{i,j}=(-1)^{i+j}v+(-1)^{i-1}a_{i,1}+(-1)^{j-1}a_{1,j}-a_{1,1}
(−1)i+jai,j=(−1)i+jv+(−1)i−1ai,1+(−1)j−1a1,j−a1,1
于是此时可以记
r
i
=
(
−
1
)
i
−
1
a
i
,
1
(
i
≥
1
)
,
c
j
=
(
−
1
)
j
a
1
,
j
(
j
>
1
)
r_i=(-1)^{i-1}a_{i,1}\;(i\ge 1),\;c_j=(-1)^{j}a_{1,j}\;(j>1)
ri=(−1)i−1ai,1(i≥1),cj=(−1)ja1,j(j>1),那么有
(
−
1
)
i
+
j
a
i
,
j
=
(
−
1
)
i
+
j
v
+
r
i
−
c
j
−
a
1
,
1
(-1)^{i+j}a_{i,j}=(-1)^{i+j}v+r_i-c_j-a_{1,1}
(−1)i+jai,j=(−1)i+jv+ri−cj−a1,1
我们的目标无非是让
(
−
1
)
i
+
j
a
i
,
j
(-1)^{i+j}a_{i,j}
(−1)i+jai,j 在某个范围内。也就是说,现在我们有一个关于 三个 变元的不等式。这好像挺难的?
别慌,先 完整地考虑问题。事实上我们还需要 a i , 1 a_{i,1} ai,1 和 a 1 , j a_{1,j} a1,j 也在合法范围内,即 r i r_i ri 在某个范围内、 c j c_j cj 在某个范围内。这是一个关于 一个 变元的不等式。
问题似乎是这样的:三个变元太难,而一个变元太简单。能不能将二者折中?又看到 a 1 , 1 a_{1,1} a1,1 恰好存在于全部的三变元不等式中,可以想办法让其 移动到一元不等式。
接下来是 见证奇怪的时刻:令
c
j
′
=
a
1
,
1
+
c
j
c'_j=a_{1,1}+c_j
cj′=a1,1+cj 则
(
−
1
)
i
+
j
a
i
,
j
=
(
−
1
)
i
+
j
v
+
r
i
−
c
j
′
(-1)^{i+j}a_{i,j}=(-1)^{i+j}v+r_i-c'_j
(−1)i+jai,j=(−1)i+jv+ri−cj′
那么三元不等式变成了
r
i
−
c
j
′
r_i-c_j'
ri−cj′ 的不等式;原来的一元不等式
c
j
∈
c_j\in
cj∈ 某个范围,变为
c
j
=
c
j
′
−
a
1
,
1
=
c
j
′
−
r
1
∈
c_j=c'_j-a_{1,1}=c'_j-r_1\in
cj=cj′−a1,1=cj′−r1∈ 某个范围,成为了二元不等式。
所以现在就是简单 差分约束 了。有负边,用 s p f a \rm spfa spfa 求解。时间复杂度 O [ T ( n + m ) 3 ] \mathcal O[T(n{\rm+}m)^3] O[T(n+m)3],由于跑不满,可以通过。
注意:不要使用邻接表。图近乎完全图,邻接矩阵更快。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long int_;
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; '0'>c||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
void writeUnsigned(const unsigned &x){
if(x > 9) writeUnsigned(x/10);
putchar((x-x/10*10)^48);
}
inline void writeint(const int &x){
if(x < 0){
putchar('-');
writeUnsigned(-x);
}
else writeUnsigned(x);
}
const int MAXN = 305;
int g[MAXN<<1][MAXN<<1];
inline void addEdge(int a,int b,int c){
if(c < g[a][b]) g[a][b] = c;
}
const int infty = (1<<30)-1;
int dis[MAXN<<1], cnt[MAXN<<1];
bool inque[MAXN<<1];
int q[1<<20]; // something big
bool spfa(int x,int n){
int *fro = q, *bac = fro+1;
fill(dis+1,dis+n+1,infty); // clear
memset(inque+1,0,n), cnt[x] = 0;
for(dis[x]=0,*fro=x; fro!=bac; ){
inque[x = *fro] = false; ++ fro;
rep(i,1,n) // neighbour matrix
if(dis[i] > dis[x]+g[x][i]){
dis[i] = dis[x]+g[x][i];
cnt[i] = cnt[x]+1;
if(cnt[i] == n)
return false; // cycle
if(inque[i] == false){
inque[i] = true;
*bac = i; ++ bac;
}
}
}
return true;
}
const int V = 1000000;
int b[MAXN][MAXN];
int main(){
for(int T=readint(),n,m; T; --T){
n = readint(), m = readint();
const int zero = n+m; // representing 0
rep(i,1,zero) rep(j,1,zero)
g[i][j] = infty;
bool ok = true;
rep(i,1,n-1) rep(j,1,m-1){
b[i][j] = readint()-b[i-1][j]-b[i][j-1]-b[i-1][j-1];
if(b[i][j] > 0 && (b[i][j]>>22)) ok = false;
if(b[i][j] < 0 && ((-b[i][j])>>22)) ok = false;
if(((i^j)&1) == 1){ // r_i - c'_j - b = -a
addEdge(n+j,i+1,b[i][j]);
addEdge(i+1,n+j,V-b[i][j]);
}
else{ // r_i - c'_j + b = a
addEdge(n+j,i+1,V-b[i][j]);
addEdge(i+1,n+j,b[i][j]);
}
}
if(ok == false){ puts("NO"); continue; }
for(int i=1; i<=n; i+=2)
addEdge(zero,i,V), addEdge(i,zero,0);
for(int i=2; i<=n; i+=2)
addEdge(zero,i,0), addEdge(i,zero,V);
for(int j=3; j<=m; j+=2)
addEdge(1,n+j-1,0), addEdge(n+j-1,1,V);
for(int j=2; j<=m; j+=2)
addEdge(1,n+j-1,V), addEdge(n+j-1,1,0);
if(spfa(zero,zero) == false) puts("NO");
else{
puts("YES");
writeint(dis[1]-dis[zero]);
rep(j,2,m){
int t = dis[n+j-1]-dis[1];
if(j&1) t = -t; // (-1)^j
putchar(' '), writeint(t);
}
putchar('\n');
rep(i,2,n){
if(i&1) writeint(dis[i]-dis[zero]);
else writeint(dis[zero]-dis[i]);
rep(j,2,m){
int t = dis[i]-dis[n+j-1];
if((i^j)&1) t = -t;
putchar(' ');
writeint(b[i-1][j-1]+t);
}
putchar('\n');
}
}
}
return 0;
}
后记
做差分约束的题,被虐了。于是来做这题,作为复习。结果还是卡在三元不等式那一步,看了大佬的题解才懂。结果又因为使用邻接表,一直 T L E \rm TLE TLE,心态崩了……