[省选联考 2021 A 卷] 矩阵游戏

前言

缓慢填坑ing

差分约束的题还是见少了,完全没思路。

Acetyl orz

题目

洛谷

讲解

首先我们不考虑 \(10^6\) 这个限制,显然做法很多,比如强制 \(a_{n,i}=a_{j,m}=0\),然后递推一下就好了。

然后调整解决问题。

可以发现一行如果 \(+r\ -r\ +r\ -r,...\),并不会对 \(b\) 矩阵产生影响,同理,一列也没有影响,于是我们可以构造出这么一个矩阵表示调整值:

\[\begin{pmatrix}&r_1+c_1\ &-r_1+c_2\ &r_1+c_3,&\cdots\\&r_2-c_1\ &-r_2-c_2\ &r_2-c_3,&\cdots\\&r_3+c_1\ &-r_3+c_2\ &r_3+c_3,&\cdots\\&\vdots&\vdots&\vdots&\ddots\\\end{pmatrix} \]

但这个矩阵的形式并不好看,我们微调一下:

\[\begin{pmatrix}&r_1-c_1\ &c_2-r_1\ &r_1-c_3,&\cdots\\&c_1-r_2\ &r_2-c_2\ &c_3-r_2,&\cdots\\&r_3-c_1\ &c_2-r_3\ &r_3-c_3,&\cdots\\&\vdots&\vdots&\vdots&\ddots\\\end{pmatrix} \]

这样看上去就统一多了,然后直接套差分约束即可。

具体的,上述矩阵为 \(\Delta_{i,j}=X-Y\) 的形式,可以得到不等式 \(0\le a_{i,j}+\Delta_{i,j}\le 10^6\),再转换一下变成 \(-a_{i,j}\le \Delta_{i,j}\le 10^6-a_{i,j}\),连边即可。

值得注意的是如果使用裸的SPFA判负环会TLE,你可使用上文提到的巨佬的deque优化写法,也可以使用洛谷上评测更快的stack写法。个人经验是如果用SPFA判负环,stack会快很多。

虽然感觉时间复杂度很离谱,但是可以通过。

代码

代码不长,容易理解
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 305;
const LL INF = 1ll << 60;
int n,m;
int a[MAXN][MAXN],b[MAXN][MAXN];
bool inq[MAXN<<1];
LL dis[MAXN<<1];

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int head[MAXN<<1],tot,cnt[MAXN<<1];
struct edge
{
	int v,w,nxt;
}e[MAXN*MAXN*4];
void Add_Edge(int x,int y,int z)
{
	e[++tot] = edge{y,z,head[x]};
	head[x] = tot;
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	for(int T = Read(); T ;-- T)
	{
		n = Read(); m = Read();
		for(int i = 1;i < n;++ i)
			for(int j = 1;j < m;++ j)	
				b[i][j] = Read();
		for(int i = 1;i <= m;++ i) a[n][i] = 0;
		for(int i = 1;i <= n;++ i) a[i][m] = 0;
		for(int i = n-1;i >= 1;-- i)
			for(int j = m-1;j >= 1;-- j)
				a[i][j] = b[i][j] - a[i+1][j] - a[i][j+1] - a[i+1][j+1];
		for(int i = 1;i <= n+m;++ i) head[i] = cnt[i] = 0,inq[i] = 0,dis[i] = INF; tot = 0;
		for(int i = 1;i <= n;++ i)
			for(int j = 1;j <= m;++ j)
			{
				int MIN = -a[i][j],MAX = 1000000-a[i][j];
				if((i+j)&1) //c-r
				{
					Add_Edge(n+j,i,-MIN);
					Add_Edge(i,n+j,MAX);
				}
				else //r-c
				{
					Add_Edge(i,n+j,-MIN);
					Add_Edge(n+j,i,MAX);
				}
			}
		stack<int> q; q.push(1); dis[1] = 0;
		bool nonono = 0;
		while(!q.empty())
		{
			int x = q.top(); q.pop(); inq[x] = 0;
			for(int i = head[x],v; i && !nonono ;i = e[i].nxt)
			{
				v = e[i].v;
				if(dis[x]+e[i].w < dis[v])
				{
					dis[v] = dis[x] + e[i].w;
					if(!inq[v]) 
					{
						q.push(v);
						++cnt[v];
						if(cnt[v] > n+m) nonono = 1;
						inq[v] = 1;
					}
				}
			}
		}
		if(nonono) {printf("NO\n");continue;}
		printf("YES\n");
		for(int i = 1;i <= n;++ i,putchar('\n'))
			for(int j = 1;j <= m;++ j)
			{
				if((i+j)&1) //c-r
					Put(a[i][j]+dis[n+j]-dis[i],' ');
				else 
					Put(a[i][j]+dis[i]-dis[n+j],' ');
			}
	}
	return 0;
}
posted @ 2021-10-26 19:48  皮皮刘  阅读(164)  评论(0编辑  收藏  举报