[SCOI2012]奇怪的游戏

题目

话说有没有跟我一样直接猜了一个最大值不会改变这样一个二乎乎的结论之后交上去保龄的呀

首先看到棋盘,选择相邻的格子,非常经典的黑白染色

显然那个二乎乎的结论是错的,随便就能\(hack\)

于是我们二分这个最大值

如果当前二分出来的最大值是\(mid\)\(i+j\)为奇数,起点连\(mid-a[i][j]\)的边,否则向终点连\(mid-a[i][j]\)的边,相邻的格子连流量无穷的边,跑最大流看看起点连出去的边和连向终点的边是否都满流就好了

但是这样仅限于\(n\times m\)为偶数的情况

因为之后当\(n\times m\)为偶数的时候我们可以把整个棋盘整体加\(1\),因此存在单调性,于是可以二分

但是当\(n\times m\)为奇数的时候我们无论如何都得空至少一个

我们可以考虑一下最后棋盘变成了\(x\)

那么就会存在

\[x\times num_{\text{奇}}-sum_{\text{奇}}=x\times num_{\text{偶}}-sum_{\text{偶}} \]

这个其实还是来源于上面的网络流建图,就是让两边流量平衡,由于\(num_{\text{奇}}!=num_{\text{偶}}\),我们可以直接解得

\[x=\frac{sum_{\text{奇}}-sum_{\text{偶}}}{num_{\text{奇}}-num_{\text{偶}}} \]

由于我们不能再让棋盘整体加了,于是直接判断\(x\)是否合法就好了

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=2005;
const LL inf=5e12;
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
std::queue<int> q;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
int head[maxn],d[maxn],id[51][51],S,T,num;
int n,m,a[51][51],pd[maxn],cur[maxn];
struct E{int v,nxt;LL f;}e[maxn*40];
inline void C(int x,int y,LL f) {
	e[++num].v=y;e[num].nxt=head[x];
	head[x]=num;e[num].f=f;
}
inline void add(int x,int y,LL f) {C(x,y,f),C(y,x,0);}
inline int BFS() {
	for(re int i=S;i<=T;i++) d[i]=0,cur[i]=head[i];
	d[S]=1,q.push(S);
	while(!q.empty()) {
		int k=q.front();q.pop();
		for(re int i=head[k];i;i=e[i].nxt)
		if(e[i].f&&!d[e[i].v]) d[e[i].v]=d[k]+1,q.push(e[i].v);
	}
	return d[T];
}
LL dfs(int x,LL now) {
	if(x==T||!now) return now;
	LL flow=0,ff;
	for(re int& i=cur[x];i;i=e[i].nxt)
	if(d[e[i].v]==d[x]+1) {
		ff=dfs(e[i].v,min(now,e[i].f));
		if(ff<=0) continue;
		now-=ff,flow+=ff,e[i].f-=ff,e[i^1].f+=ff;
		if(!now) break;
	}
	return flow;
}
inline int check(LL mx) {
	num=1;memset(head,0,sizeof(head));
	for(re int i=1;i<=n;i++)
		for(re int j=1;j<=m;j++) {
			pd[id[i][j]]=num+1;
			if((i+j)&1) add(S,id[i][j],mx-a[i][j]);
				else add(id[i][j],T,mx-a[i][j]);
			if(mx<a[i][j]) return 0;
		}
	for(re int i=1;i<=n;i++)
		for(re int j=1;j<=m;j++) {
			if(!((i+j)&1)) continue;
			for(re int k=0;k<4;k++) {
				int x=i+dx[k],y=j+dy[k];
				if(x<1||y<1||x>n||y>m) continue;
				add(id[i][j],id[x][y],inf);
			}
		}
	while(BFS()) dfs(S,inf);
	int flag=1;
	for(re int i=1;i<=n;i++)	
		for(re int j=1;j<=m;j++)
			flag&=(e[pd[id[i][j]]].f==0);
	return flag;
}
int main() {
	int Test=read();
	while(Test--) {
		n=read(),m=read();T=0;
		for(re int i=1;i<=n;i++)
			for(re int j=1;j<=m;j++) a[i][j]=read();
		for(re int i=1;i<=n;i++)
			for(re int j=1;j<=m;j++) id[i][j]=++T;
		++T;
		LL s[2],tot[2];
		tot[0]=tot[1]=s[0]=s[1]=0;
		for(re int i=1;i<=n;i++)
			for(re int j=1;j<=m;j++)
				tot[(i+j)&1]++,s[(i+j)&1]+=a[i][j];
		if((n*m)&1) {
			LL x=(s[1]-s[0])/(tot[1]-tot[0]);
			if(check(x)) 
				printf("%lld\n",x*tot[1]-s[1]);
			else puts("-1");			
		}
		else {
			LL ans=-1,l=1,r=2e9;
			for(re int i=1;i<=n;i++)
				for(re int j=1;j<=m;j++) l=max(l,a[i][j]);
			while(l<=r) {
				LL mid=l+r>>1;
				if(check(mid)) r=mid-1,ans=mid;
					else l=mid+1;
			}
			if(ans==-1) puts("-1");
			else printf("%lld\n",ans*tot[1]-s[1]);
		}
	}
	return 0;
}
posted @ 2019-03-25 14:57  asuldb  阅读(187)  评论(0编辑  收藏  举报