[省选联考2021]矩阵游戏

122 篇文章 0 订阅

题目

传送门 to luogu

思路

显然我们只需要确定 a i , 1    ( i ≥ 1 ) a_{i,1}\;(i\ge 1) ai,1(i1) 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)j1ai,1+(1)i1a1,j+(1)i+j1a1,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=bi1,j1vi1,jvj,i1vi1,j1,这是由 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)i1ai,1+(1)j1a1,ja1,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)i1ai,1(i1),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+ricja1,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+ricj
那么三元不等式变成了 r i − c j ′ r_i-c_j' ricj 的不等式;原来的一元不等式 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=cja1,1=cjr1 某个范围,成为了二元不等式。

所以现在就是简单 差分约束 了。有负边,用 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,心态崩了……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值