洛谷P7515:矩阵游戏(差分约束)

解析

如果没有元素均要在 [ 0 , 1 e 6 ] [0,1e6] [0,1e6] 的条件,可以很容易的构造出一个合法解。

那么我们就要通过调整得到的解,使所有数都在合法范围内。
注意到,每次给某一行/列依次+1,-1,+1,-1…这样仍然符合要求。
让每一行/列错开,就能变成:

row:
+-+-+
-+-+-
+-+-+
-+-+-

column:
-+-+-
+-+-+
-+-+-
+-+-+

这样每个格子行贡献和列贡献的符号就都是相反的,然后就可以差分约束了。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;

const int N=650;
const int M=50050;
const int mod=1e9+7;
const double eps=1e-9;

inline ll read() {
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}

int n,m;
int a[N][N],b[N][N],c[N],d[N],tot;
ll dis[N];
int o=1e6;
queue<int>q;
bool vis[N];
int tim[N];
struct node{
	int to,nxt,w;
}p[N*N*2];
int fi[N],cnt;
inline void addline(int x,int y,int w){
	p[++cnt]=(node){y,fi[x],w};fi[x]=cnt;
}
bool spfa(){
	memset(dis,0,sizeof(dis));
	memset(tim,0,sizeof(tim));
	for(int i=1;i<=tot;i++) q.push(i),vis[i]=1;
	while(!q.empty()){
		int now=q.front();q.pop();
		vis[now]=0;
		for(int i=fi[now];~i;i=p[i].nxt){
			int to=p[i].to;
			if(dis[to]<dis[now]+p[i].w){
				dis[to]=dis[now]+p[i].w;
				tim[to]++;
				if(!vis[to]){
					vis[to]=1;q.push(to);
				}
				if(tim[to]>tot) return false;
			}
		}
	}
	return true;
}
void work(){
	memset(a,0,sizeof(a));tot=0;
	memset(fi,-1,sizeof(fi));cnt=-1;
	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=n-1;i>=1;i--){
		for(int j=m-1;j>=1;j--) a[i][j]=b[i][j]-a[i+1][j+1]-a[i+1][j]-a[i][j+1];
	}
	//for(int i=1;i<=n;i++){
		//for(int j=1;j<=m;j++) printf("%d ",a[i][j]);
		//puts("");
	//}
	for(int i=1;i<=n;i++) c[i]=++tot;
	for(int j=1;j<=m;j++) d[j]=++tot;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if((i+j)&1){
				addline(c[i],d[j],a[i][j]-o);
				addline(d[j],c[i],-a[i][j]);
			}
			else{				
				addline(c[i],d[j],-a[i][j]);
				addline(d[j],c[i],a[i][j]-o);
			}
		}
	}
	//for(int i=1;i<=tot;i++)
	//	for(int j=1;j<=tot;j++) printf("%d -> %d w=%d\n",i,j,e[i][j]);
	
	if(!spfa()){
		puts("NO");return;
	}
	puts("YES");
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			ll add=dis[c[i]]-dis[d[j]];
			if((i+j)&1) a[i][j]+=add;
			else a[i][j]-=add;
			printf("%d ",a[i][j]);
		}
		puts("");
	}
}
int main() {
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	int T=read();
	while(T--) work();
	return 0;
}
/*
1
3 3
1000000 2000000
0 0
*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值