BZOJ2756 [SCOI2012]奇怪的游戏 【网络流 + 二分】
题目
Blinker最近喜欢上一个奇怪的游戏。
这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数。每次 Blinker 会选择两个相邻
的格子,并使这两个数都加上 1。
现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同
一个数则输出-1。
输入格式
输入的第一行是一个整数T,表示输入数据有T轮游戏组成。
每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。
接下来有N行,每行 M个数。
输出格式
对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。
输入样例
2
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
输出样例
2
-1
提示
【数据范围】
对于30%的数据,保证 T<=10,1<=N,M<=8
对于100%的数据,保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000
题解
我们将原图黑白染色就会发现每次一定是一黑一白+1
那么我们可以分类讨论:
记黑色n1个总和s1,白色n2个总和s2
①若n1 == n2
二者s1 != s2,无论如何也不会相等
二者s1 == s2,用网络流二分检验:
S向其中一种颜色建边,容量为还差的值
同时这种颜色向周围建边,容量INF
另一种颜色向T建边,容量为还差的值
看看是否满流
②若n1 != n2
我们设x为最后的值
由黑白增量相同,可以得到等式:
x∗n1−s1=x∗n2−s2
化简:
x=s1−s2n1−n2
可见x为定值,若x比最大的数小,无解
否则网络流判断一下
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 45,maxm = 1000005;
const LL INF = (1ll << 50);
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
return out * flag;
}
LL s[maxn][maxn],N,M,n1,n2,mx;
LL s1,s2,d[maxn * maxn];
int X[4] = {0,0,-1,1},Y[4] = {-1,1,0,0},c[maxn][maxn];
int h[maxn * maxn],ne,S,T,vis[maxn * maxn],cur[maxn * maxn];
struct EDGE{int to,nxt; LL f;}ed[maxm];
inline void build(int u,int v,LL w){
ed[ne] = (EDGE){v,h[u],w}; h[u] = ne++;
ed[ne] = (EDGE){u,h[v],0}; h[v] = ne++;
}
bool bfs(){
queue<int> q; int u;
for (int i = S; i <= T; i++) d[i] = INF,vis[i] = false;
d[S] = 0; vis[S] = true; q.push(S);
while (!q.empty()){
u = q.front(); q.pop();
Redge(u) if (ed[k].f && !vis[to = ed[k].to]){
d[to] = d[u] + 1; vis[to] = true; q.push(to);
}
}
return vis[T];
}
LL dfs(int u,LL minf){
if (u == T || !minf) return minf;
LL f,flow = 0; int to;
if (cur[u] == -1) cur[u] = h[u];
for (int& k = cur[u]; k; k = ed[k].nxt)
if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
ed[k].f -= f; ed[k ^ 1].f += f;
flow += f; minf -= f;
if (!minf) break;
}
return flow;
}
LL maxflow(){
LL flow = 0;
while (bfs()){fill(cur,cur + maxn * maxn,-1); flow += dfs(S,INF);}
return flow;
}
bool check(LL x){
S = 0; T = N * M + 1; ne = 2; memset(h,0,sizeof(h));
LL tot = 0;
REP(i,N) REP(j,M){
if (c[i][j]){
build(S,(i - 1) * M + j,x - s[i][j]),tot += x - s[i][j];
for (int k = 0; k < 4; k++){
int nx = i + X[k],ny = j + Y[k];
if (nx < 1 || ny < 1 || nx > N || ny > M) continue;
build((i - 1) * M + j,(nx - 1) * M + ny,INF);
}
}else build((i - 1) * M + j,T,x - s[i][j]);
}
return tot == maxflow();
}
void solve1(){
LL x = (s1 - s2) / (n1 - n2);
if (x < mx || !check(x)) puts("-1");
else printf("%lld\n",x * n1 - s1);
}
void solve2(){
if (s1 != s2) {puts("-1"); return;}
LL L = mx,R = INF,mid;
while (L <= R){
mid = L + R >> 1;
if (check(mid)) R = mid - 1;
else L = mid + 1;
}
printf("%lld\n",(LL)L * n1 - s1);
}
int main(){
int Tim = read();
while (Tim--){
N = read(); M = read(); n1 = n2 = s1 = s2 = 0; mx = 0;
REP(i,N) REP(j,M) c[i][j] = (i + j ) & 1,s[i][j] = read();
REP(i,N) REP(j,M){
if (c[i][j]) n1++,s1 += s[i][j];
else n2++,s2 += s[i][j];
mx = max(mx,s[i][j]);
}
if (n1 != n2) solve1();
else solve2();
}
return 0;
}
BZOJ2756 [SCOI2012]奇怪的游戏 【网络流 + 二分】的更多相关文章
- bzoj2756: [SCOI2012]奇怪的游戏(网络流+分情况)
2756: [SCOI2012]奇怪的游戏 题目:传送门 题解: 发现做不出来的大难题一点一个网络流 %大佬 首先黑白染色(原来是套路...)染色之后就可以保证每次操作都一定会使黑白各一个各自的值加1 ...
- BZOJ 2756: [SCOI2012]奇怪的游戏 网络流/二分
2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 1594 Solved: 396[Submit][Stat ...
- BZOJ2756 SCOI2012奇怪的游戏(二分答案+最大流)
由数据范围容易想到网络流.由于操作只是对于棋盘上相邻两格,容易想到给其黑白染色. 假设已经知道最后要变成什么数.那么给黑白点之间连边,其流量则表示同时增加的次数,再用源汇给其限流为需要增加的数即可. ...
- 【BZOJ2756】奇怪的游戏(二分,网络流)
[BZOJ2756]奇怪的游戏(二分,网络流) 题面 BZOJ Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blink ...
- Bzoj2756 [SCOI2012]奇怪的游戏
2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 3220 Solved: 886 Description ...
- BZOJ2756:[SCOI2012]奇怪的游戏(最大流,二分)
Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻 的格子,并使这两个数都加上 1. 现在 B ...
- 【BZOJ2756】奇怪的游戏(二分,最小割)
题意: Blinker最近喜欢上一个奇怪的游戏.这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻的格子,并使这两个数都加上 1.现在 Blinker 想知道最 ...
- 洛谷5038 [SCOI2012]奇怪的游戏(二分+网络流+判断奇偶)
寒假的时候就听过这个题.但是一直没有写. qwq 首先,我们发现题目中的图是个网格图,然后每次可以将相邻两个格子加一. 很容易就想到是黑白染色.那么每次操作,就相当于同时操作一个白点,一个黑点. 我们 ...
- BZOJ2756 [SCOI2012]奇怪的游戏 最大流
好久没有写博客了.不过这个博客也没有多少人看 最近在写网络流,为了加深理解,来写一两篇题解. 对整个棋盘进行黑白染色以后可以发现,一次操作就是让二分图的两个点的值分别 \(+1\). 这样,我们就可以 ...
随机推荐
- hdu_2837_Calculation(欧拉函数,快速幂求指数循环节)
Assume that f(0) = 1 and 0^0=1. f(n) = (n%10)^f(n/10) for all n bigger than zero. Please calculate f ...
- Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: Pointcut is not well-formed: expecting 'name pattern' at character position 36
例: <aop:config> <aop:pointcut expression="execution(* com.zsn.Service.Impl.*.*(..))&qu ...
- list推导式,dict推导式,set推导式
生成一个1-14的列表 1.1 普通for循环 # lst = [] # for i in range(1,15): # lst.append(i) # print(lst) # # 结果: # [1 ...
- java中substring()、charAt()、indexOf() (2013-05-05-bd 写的日志迁移
substring 1. public String substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个子字符串, 该子字符串始于指定索引处的字符,一直 ...
- js for 循环中有异步函数时,回调函数总是最后一步的值。
原因:for循环执行时不会等待异步函数执行. 解决方法: 1.改为递归函数(暂时不会). 2.构建一个自执行函数传参(匿名函数) 参考::https://www.cnblogs.com/csuwuji ...
- 13、python中的函数(闭包与装饰器)
一.嵌套函数 函数的内部又再定义另一个函数,这个函数就叫嵌套函数,里面含函数就叫内部函数. 示例: 二.返回函数 函数可以接收函数对象作为参数,同理函数也能返回一个函数对象作为返回值. 示例: 返回函 ...
- c语言的左移、右移
先说左移,左移就是把一个数的所有位都向左移动若干位,在C中用<<运算符.例如: int i = 1; i = i << 2; //把i里的值左移2位 也就是说,1的2进制是0 ...
- wireshark 安装
#yum install wireshark 安装完毕后 whereis wireshark 找不到可执行程序 /bin /sbin /usr/bin /usr/sbin下均没有. 实际上wiresh ...
- Java重写与重载
重写的规则: 参数列表必须完全与被重写方法的相同: 返回类型必须完全与被重写方法的返回类型相同: 访问权限不能比父类中被重写的方法的访问权限更低.例如:如果父类的一个方法被声明为public,那么在子 ...
- 剑指Offer - 九度1386 - 旋转数组的最小数字
剑指Offer - 九度1386 - 旋转数组的最小数字2013-11-24 01:57 题目描述: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转 ...