bzoj2756[SCOI2012]奇怪的游戏 二分 分类讨论 最大流
2017-07-30 20:50
567 查看
细节比较多的一道题目。
题意:给你一个矩阵,每次能把相邻的两个数都+1,问你最少多少次能把整个矩阵的数都变成同一个数。
网络流的模型比较明显,但是正解的话考场上可能不容易想到= =。
先把整个网格图黑白染色,设格子数和总和分别为num1,sum1,num2,sum2.
那么假设最终变成了X,那么最后的答案肯定是X*num1-sum1,即添加了多少次。
然后我们分类讨论。
num1!=num2的时候,因为有num1*x-sum1=num2*x-sum2
变形可得x=(sum1-sum2)/(num1-num2)
那么明显x唯一,直接判断一下是否可行就好了。
否则的话就二分x,连边如下:
X到白点连边,黑点到T连边,容量都是mid-a[i][j]
然后黑白点之间连,容量为inf。
看看最大流是否跑满就好。
题意:给你一个矩阵,每次能把相邻的两个数都+1,问你最少多少次能把整个矩阵的数都变成同一个数。
网络流的模型比较明显,但是正解的话考场上可能不容易想到= =。
先把整个网格图黑白染色,设格子数和总和分别为num1,sum1,num2,sum2.
那么假设最终变成了X,那么最后的答案肯定是X*num1-sum1,即添加了多少次。
然后我们分类讨论。
num1!=num2的时候,因为有num1*x-sum1=num2*x-sum2
变形可得x=(sum1-sum2)/(num1-num2)
那么明显x唯一,直接判断一下是否可行就好了。
否则的话就二分x,连边如下:
X到白点连边,黑点到T连边,容量都是mid-a[i][j]
然后黑白点之间连,容量为inf。
看看最大流是否跑满就好。
#include<algorithm> #include<cstring> #include<cstdio> #define S 0 #define T n*m+1 #define fo(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) using namespace std; const int N=1e5+5; const int M=105; typedef long long ll; const ll inf=1ll<<50; int tot,n,m; int head ,go ,next ,cur ; ll val ; const int dx[4]={0,1,0,-1}; const int dy[4]={1,0,-1,0}; int a[M][M],col[M][M],num[M][M],vis[M][M]; int q ; int num1,num2; ll sum1,sum2; int dis ; inline void add(int x,int y,ll z) { go[++tot]=y; next[tot]=head[x]; val[tot]=z; head[x]=tot; } inline void ins(int x,int y,ll z) { add(x,y,z); add(y,x,0); } bool bfs() { int t=0,w=1; memset(dis,-1,sizeof(dis)); q[1]=S,dis[S]=0; while (t<w) { int x=q[++t]; for(int i=head[x];i;i=next[i]) { int v=go[i]; if (val[i]&&dis[v]==-1) { dis[v]=dis[x]+1; q[++w]=v; } } } return dis[T]!=-1; } ll dfs(int x,ll f) { if (x==T)return f; ll w,used=0; for(int i=cur[x];i;i=next[i]) { int v=go[i]; if (dis[v]==dis[x]+1) { w=dfs(v,min(f-used,val[i])); val[i]-=w; val[i^1]+=w; if (val[i])cur[x]=i; used+=w; if (used==f)return f; } } if (!used)dis[x]=-1; return used; } inline ll dinic() { ll tmp=0; while (bfs()) { fo(i,S,T)cur[i]=head[i]; tmp+=dfs(S,inf); } return tmp; } inline bool pd(ll x) { memset(head,0,sizeof(head)); tot=1; ll cnt=0; fo(i,1,n) fo(j,1,m) if (col[i][j]) { ins(S,num[i][j],x-a[i][j]); cnt+=x-a[i][j]; fo(k,0,3) { int x=i+dx[k]; int y=j+dy[k]; if (x>=1&&x<=n&&y>=1&&y<=m) { ins(num[i][j],num[x][y],inf); } } } else ins(num[i][j],T,x-a[i][j]); if(dinic()==cnt)return 1; else return 0; } int main() { int cas; scanf("%d",&cas); while (cas--) { scanf("%d%d",&n,&m); num1=num2=sum1=sum2=0; int mx=0; fo(i,1,n) fo(j,1,m)scanf("%d",&a[i][j]),mx=max(mx,a[i][j]),num[i][j]=(i-1)*m+j,col[i][j]=(i+j)%2; fo(i,1,n) fo(j,1,m) if (col[i][j])num1++,sum1+=a[i][j]; else num2++,sum2+=a[i][j]; if (num1!=num2) { ll x=(sum2-sum1)/(num2-num1); if (x>=mx&&pd(x)) { printf("%lld\n",x*num1-sum1); continue; } printf("-1\n"); } else { if(sum1!=sum2) { printf("-1\n"); continue; } ll l=mx,r=inf; while (l<=r) { ll mid=(l+r)>>1; if (pd(mid))r=mid-1; else l=mid+1; } printf("%lld\n",1ll*l*num1< 17c8c /span>-sum1); } } }
相关文章推荐
- 【BZOJ 2756】[SCOI2012]奇怪的游戏 二分+最大流
- BZOJ 2756: [SCOI2012]奇怪的游戏 [最大流 二分]
- 【bzoj2756】【SCOI2012】【奇怪的游戏】【最大流+二分】
- 【二分+最大流】[SCOI2012]奇怪的游戏 BZOJ2756
- 【最大流】【二分检验】【SCOI2012】奇怪的游戏
- 【最大流】【二分】[Scoi2012] bzoj2756 奇怪的游戏
- 【BZOJ-2756】奇怪的游戏 最大流 + 分类讨论 + 二分
- bzoj2756: [SCOI2012]奇怪的游戏 二分+最大流
- bzoj 2756 [SCOI2012]奇怪的游戏【二分+最大流】
- BZOJ 2756 SCOI 2012 奇怪的游戏 二分+最大流
- BZOJ2756 [SCOI2012]奇怪的游戏 【网络流 + 二分】
- Dinic最大流(bzoj 2756: [SCOI2012]奇怪的游戏)
- bzoj 2756: [SCOI2012]奇怪的游戏(网络流+二分)
- 【BZOJ 2756】[SCOI2012]奇怪的游戏 网络流+二分
- 【SCOI2012】【二分法】【最大流】奇怪的游戏
- 【BZOJ2756】【SCOI2012】奇怪的游戏 最大流、
- bzoj2756 [SCOI2012]奇怪的游戏
- bzoj2756 [SCOI2012]奇怪的游戏
- BZOJ 2756 [SCOI2012]奇怪的游戏 二分+网络流
- BZOJ2756 【scoi2012】奇怪的游戏(二分+网络流)