【BZOJ 2756】[SCOI2012]奇怪的游戏 网络流+二分

来源:互联网 发布:淘宝添加到桌面没有了 编辑:程序博客网 时间:2024/05/01 20:43

很有趣的做法。

首先,题目要求每一次的加数都必须加相邻的两个格子,这样我们将棋盘黑白染色就能很显然的发现每一次的加数都是将一个黑格子和一个白格子加一。

继续分析:设黑格子的个数为c1,和为s1,白格子的个数为c2,和为s2,设最后棋盘所有数字为x

x*c1-s1=x*c2-s2

x=(s1-s2)/(c1-c2)

1.c1!=c2直接网络流判断

2.c1!=c2显然如果棋盘现在所有数为x,由于c1==c2所以可以将每个格子都再加一,也就是说满足单调性,这样就可以二分了。

最后网络流怎么判呢?很简单

s->黑格子,权值为x-val     白格子->t权值为x-val  黑白之间连边,权值为inf,最后判断是否满流就好了

#include<cstdio>#include<cstring>#include<iostream>#define maxn 162121#define inf 36028797018963967ll#define LL long longusing namespace std;const int dx[]={-1,0,0,1};const int dy[]={0,1,-1,0};int T,last[maxn],head[1641],n,m,tot,h[maxn],nu[41][41],vis[41][41];int s,t,q[maxn];LL mat[41][41];struct edge{int v,next;LL w;}e[maxn];void adde(int a,int b,LL c){e[tot].v=b,e[tot].next=head[a],e[tot].w=c;head[a]=tot++;e[tot].v=a,e[tot].next=head[b],e[tot].w=0;head[b]=tot++;} bool bfs(){for(int i=s;i<=t;i++)h[i]=-1;h[s]=0;q[0]=s;int l=0,r=1;while(l<r){int u=q[l++];for(int v,i=head[u];i!=-1;i=e[i].next)if(h[v=e[i].v]==-1&&e[i].w){h[v]=h[u]+1;q[r++]=v;}}return h[t]!=-1;}LL dfs(int u,LL f){if(!f||u==t)return f;LL used=0,w;for(int v,i=last[u];i!=-1;i=e[i].next)if(h[v=e[i].v]==h[u]+1&&e[i].w){w=min(f-used,e[i].w);last[u]=i;w=dfs(v,w);used+=w;e[i].w-=w,e[i^1].w+=w;if(used==f)return f;}if(!used)h[u]=-1;return used;}LL dinic(){LL ans=0;while(bfs()){for(int i=s;i<=t;i++)last[i]=head[i];ans+=dfs(s,inf);}return ans;}bool cheak(LL x){tot=0;s=0,t=n*m+1;LL ans=0;memset(head,-1,sizeof(head));for(int id,i=1;i<=n;i++){for(int j=1;j<=m;j++){id=nu[i][j];if(vis[i][j]){ans+=x-mat[i][j];adde(s,nu[i][j],x-mat[i][j]);for(int x,y,k=0;k<4;k++){x=i+dx[k],y=j+dy[k];if(x<1||x>n||y<1||y>m)continue;adde(id,nu[x][y],inf);}}else adde(id,t,x-mat[i][j]);}}return ans==dinic();}void solve(){LL s1=0,c1=0,s2=0,c2=0,x=0,mx=0;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){scanf("%lld",&mat[i][j]);mx=max(mx,mat[i][j]);vis[i][j]=i+j&1;nu[i][j]=(i-1)*m+j;if(vis[i][j])c1++,s1+=mat[i][j];else c2++,s2+=mat[i][j];}}if(c1!=c2){x=(s1-s2)/(c1-c2);if(x>=mx&&cheak(x))printf("%lld\n",x*c1-s1);else puts("-1");}else{if(s1!=s2)puts("-1");else{LL l=mx,r=inf;while(l<r){LL mid=l+r>>1ll;if(cheak(mid))r=mid;else l=mid+1;}printf("%lld\n",l*c1-s1);}}}int main(){scanf("%d",&T);while(T--)solve();return 0;}


0 0