洛谷 P7515 - [省选联考 2021 A 卷] 矩阵游戏(差分约束)
emmm……怎么评价这个题呢,赛后学完差分约束之后看题解感觉没那么 dl,可是现场为啥就因为种种原因想不到呢?显然是 wtcl(
先不考虑“非负“及” \(\le 10^6\) ”这两条件,考虑什么样的矩阵 \(A\) 能够生成 \(B\),我们首先考虑矩阵 \(A\) 的一个特解 \(A'\):第一行和第一列都是 \(0\) 的情况,显然 \(A'\) 可以通过一遍反着递推求出。接下来考虑怎样通过 \(A'\) 推出所有符合要求的矩阵 \(A\),一个显然的事实是如果确定了该矩阵 \(A\) 的第一行和第一列,那么最终的矩阵就定下来了。注意到 \(A\) 的第一行第一列的元素 \(A_{11}\) 是有点特别的,因为它既在第一行又在第一列,我们先忽略它,暂且将它定为 \(0\)。我们考虑对于某个 \(i\ne 1\),将 \(A_{1i}\)(或 \(A_{i1}\))由 \(0\) 变为 \(v\) 对整个矩阵 \(A\) 产生的影响,以 \(A_{1i}\) 为例,手玩几组数据就可以发现若 \(A_{1i}\) 变成 \(v\),那么 \(A_{2i}\) 会减去 \(v\),\(A_{3i}\) 会加上 \(v\),\(A_{4i}\) 又会减去 \(v\),以此类推。我们假设 \(x_i=A_{i1},y_i=A_{1i}\),那么
考虑加上 \(A_{11}\) 之后的影响,那么 \(A_{ij}\) 会加上 \((-1)^{i+j+1}A_{11}\),这个看起来有点棘手,怎么办呢?我们考虑设 \(A_{11}=x_1+y_1\)(这个 \(x_1\) 可以为任意整数),我们令新的 \(x_i\leftarrow x_i+(-1)^{i}y_1\),再令新的 \(y_i\leftarrow y_i+(-1)^ix_1\),不难发现经过这样的转化之后,\(A_{i,j}\) 由原来的 \(A'_{i,j}+(-1)^{j+1}x_i+(-1)^{i+1}y_j\) 变为 \(A'_{i,j}+(-1)^{j+1}(x_i+(-1)^iy_1)+(-1)^{i+1}(y_j+(-1)^jx_1)=A'_{i,j}+(-1)^{j+1}x_i+(-1)^{i+1}y_j+(-1)^{i+j+1}(x_1+y_1)\)(注:该式子中的 \(x_i,y_i\) 为原来的 \(x_i,y_i\),下同)刚好就是新的 \(A_{i,j}\),但是这样第一行第一列的值就不对了,此时考虑再令 \(A_{i1}=x_i+(-1)^{i+1}y_1\),\(A_{1i}=y_i+(-1)^{i+1}x_1\),这样新的 \(A_{1i}=x_i+(-1)^{i}y_1+(-1)^{i+1}y_1\) 刚好就是原来的 \(x_i\),\(A_{i1}\) 也同理。因此新的 \(A_{i,j}=A'_{i,j}+(-1)^{j+1}x_i+(-1)^{i+1}y_j\),就不用考虑 \(i,j\) 是否为 \(1\),显然一组 \(x,y\) 对应一个生成 \(B\) 的矩阵 \(A\),而一个对于 \(A\) 又存在合法的 \(x,y\),因此我们只用求出合法的 \(x_i,y_j\) 即可构造出符合要求的 \(A\)
我们考虑 \(A_{i,j}\) 非负这个条件的具体作用,由于涉及 \((-1)^i\) 这种形式的东西,因此按 \(i,j\) 的奇偶性分类讨论:
- \(i\) 为奇数,\(j\) 为奇数,\(0\le x_i+y_j+A'_{i,j}\le 10^6\)
- \(i\) 为奇数,\(j\) 为偶数,\(0\le -x_i+y_j+A'_{i,j}\le 10^6\)
- \(i\) 为偶数,\(j\) 为奇数,\(0\le x_i-y_j+A'_{i,j}\le 10^6\)
- \(i\) 为偶数,\(j\) 为偶数,\(0\le -x_i-y_j+A'_{i,j}\le 10^6\)
中间两个都可通过某种方式转化为差分约束的标准形式,关键是前两个怎么处理,其实也比较 simple,我们令 \(x'_i=(-1)^{i+1}x_i,y'_i=(-1)^iy_i\),那么上面四种情况又可写作:
- \(i\) 为奇数,\(j\) 为奇数,\(0\le x'_i-y'_j+A'_{i,j}\le 10^6\)
- \(i\) 为奇数,\(j\) 为偶数,\(0\le -x'_i+y'_j+A'_{i,j}\le 10^6\)
- \(i\) 为偶数,\(j\) 为奇数,\(0\le -x'_i+y'_j+A'_{i,j}\le 10^6\)
- \(i\) 为偶数,\(j\) 为偶数,\(0\le x'_i-y'_j+A'_{i,j}\le 10^6\)
这下四个式子都可以转化为差分约束的形式了,直接差分约束建出图来判个负环即可。
时间复杂度 \(Tn^3\)
这里有一个小小的细节以前一直没有注意过,这里稍微提一下,就是判负环过程中的“松弛次数”只要 \(dis\) 值被更新了就行,不一定指“入队次数”,这样常数会小一些,否则对于 NO 的数据可能要跑很久,我一直为此 TLE 50,交了 \(114514191981019260817998244353\) 发(bushi)
代码异常好写:
const int MAXN=300;
const int MAXV=600;
const int MAXE=300*300*2;
int n,m,a[MAXN+5][MAXN+5],b[MAXN+5][MAXN+5];
int hd[MAXV+5],to[MAXE+5],nxt[MAXE+5],val[MAXE+5],ec=0;
void adde(int u,int v,int w){to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
bool inq[MAXV+5];ll dis[MAXV+5];int in[MAXV+5];
void clear(){
memset(b,0,sizeof(b));
memset(hd,0,sizeof(hd));ec=0;
memset(in,0,sizeof(in));
memset(dis,0,sizeof(dis));
memset(inq,0,sizeof(inq));//注意清空 inq 数组,因为有可能出现判到负环就 return 的清空,所以最后 inq[i] 不一定是 0
}
void solve(){
scanf("%d%d",&n,&m);clear();
for(int i=1;i<n;i++) for(int j=1;j<m;j++) scanf("%d",&a[i][j]);
for(int i=2;i<=n;i++) for(int j=2;j<=m;j++) b[i][j]=a[i-1][j-1]-b[i-1][j]-b[i][j-1]-b[i-1][j-1];
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
if(~(i+j)&1) adde(i,j+n,b[i][j]),adde(j+n,i,1e6-b[i][j]);//x[i]-y[j]
else adde(j+n,i,b[i][j]),adde(i,j+n,1e6-b[i][j]);//y[j]-x[i]
} queue<int> q;
for(int i=1;i<=n+m;i++) q.push(i),inq[i]=1;
while(!q.empty()){
int x=q.front();q.pop();inq[x]=0;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];
if(dis[y]>dis[x]+z){
dis[y]=dis[x]+z;
if(++in[y]>n+m) return puts("NO"),void();//这边稍微注意下,不一定要写到下面一个 if 中
if(!inq[y]) q.push(y),inq[y]=1;
}
}
}
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
if(~(i+j)&1) b[i][j]+=dis[i]-dis[j+n];
else b[i][j]+=dis[j+n]-dis[i];
} printf("YES\n");
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) printf("%d%c",b[i][j]," \n"[j==m]);
}
int main(){int qu;scanf("%d",&qu);while(qu--) solve();return 0;}

浙公网安备 33010602011771号