BZOJ 2756: [SCOI2012]奇怪的游戏
2017-01-22 10:17
387 查看
2756: [SCOI2012]奇怪的游戏
Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 3410 Solved: 941
[Submit][Status][Discuss]
Description
Blinker最近喜欢上一个奇怪的游戏。这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数。每次 Blinker 会选择两个相邻
的格子,并使这两个数都加上 1。
现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同
一个数则输出-1。
Input
输入的第一行是一个整数T,表示输入数据有T轮游戏组成。每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。
接下来有N行,每行 M个数。
Output
对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。Sample Input
22 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
Sample Output
2-1
HINT
【数据范围】对于30%的数据,保证 T<=10,1<=N,M<=8
对于100%的数据,保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000
Source
[Submit][Status][Discuss]二分答案 + 网络流判断
不难看出,对棋盘进行黑白染色之后,每次相加都会使得相邻的一黑一白两个格子同时+1,。
先对所有黑白格子进行统计,处理出$cnt0$,$cnt1$分别代表黑、白格子个数,处理出$sum0$,$sum1$分别代表黑、白格子权值和。
分类讨论如下:
如果$cnt0=cnt1$
如果$sum0\not =sum1$,那么一定不存在合法解
如果$sum0=sum1$,设最终所有数字都变成$ans$,发现如果$ans$满足,则$ans+1$必定也满足,即$ans$满足二分性质,因此可以二分答案,网络流判断是否可行。
如果$cnt0\not =cnt1$
依旧设最终数字为$ans$,则有$cnt0\times ans-sum0=cnt1\times ans-sum1$,化简得$ans=\frac{sum0-sum1}{cnt0-cnt1}$,只需要检查这个值是否合法即可。
网络流判断可行的方法:
从S向每个黑点连$ans-Val_{i,j}$的边,从每个白点向T连$ans-Val_{i,j}$的边,相邻的黑点向白点连$+\infty$的边,判断是否满流。
#include <bits/stdc++.h> inline char nextChar(void) { static const int siz = 1 << 20; static char buf[siz]; static char *hd = buf + siz; static char *tl = buf + siz; if (hd == tl) fread(hd = buf, 1, siz, stdin); return *hd++; } inline int nextInt(void) { register int ret = 0; register bool neg = false; register char bit = nextChar(); for (; bit < 48; bit = nextChar()) if (bit == '-')neg ^= true; for (; bit > 47; bit = nextChar()) ret = ret * 10 + bit - '0'; return neg ? -ret : ret; } typedef long long lnt; const int siz = 50; const int pnt = 2000; const int edg = 2000005; const lnt inf = 2e18 + 9; int tot; int S, T; int hd[pnt]; int nt[edg]; int to[edg]; lnt fl[edg]; inline void addEdge(int u, int v, lnt f) { nt[tot] = hd[u]; to[tot] = v; fl[tot] = f; hd[u] = tot++; nt[tot] = hd[v]; to[tot] = u; fl[tot] = 0; hd[v] = tot++; } int dep[pnt]; inline bool bfs(void) { static int que[pnt]; static int head, tail; memset(dep, 0, sizeof(dep)); que[head = 0] = S, tail = dep[S] = 1; while (head != tail) { int u = que[head++], v; for (int i = hd[u]; ~i; i = nt[i]) if (fl[i] && !dep[v = to[i]]) dep[que[tail++] = v] = dep[u] + 1; } return dep[T]; } int cur[pnt]; inline lnt min(lnt a, lnt b) { return a < b ? a : b; } lnt dfs(int u, lnt f) { if (!f || u == T) return f; lnt used = 0, flow; for (int i = cur[u], v; ~i; i = nt[i]) if (fl[i] && dep[v = to[i]] == dep[u] + 1) { flow = dfs(v, min(fl[i], f - used)); used += flow; fl[i] -= flow; fl[i^1] += flow; if (fl[i]) cur[u] = i; if (used == f) return f; } if (!used) dep[u] = 0; return used; } inline lnt maxFlow(void) { lnt maxFlow = 0, newFlow; while (bfs()) { memcpy(cur, hd, sizeof(hd)); while (newFlow = dfs(S, inf)) maxFlow += newFlow; } return maxFlow; } int n, m; lnt num[siz][siz]; const int mv[4][2] = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} }; inline bool legal(int x, int y) { if (x < 1 || x > n)return false; if (y < 1 || y > m)return false; return true; } inline int pos(int x, int y) { return (x - 1) * m + y; } inline bool check(lnt ans) { lnt sum = 0; S = 0, T = n * m + 1; memset(hd, -1, sizeof(hd)), tot = 0; for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) { if ((i ^ j) & 1) { sum += ans - num[i][j]; addEdge(S, pos(i, j), ans - num[i][j]); for (int k = 0; k < 4; ++k) { int x = i + mv[k][0]; int y = j + mv[k][1]; if (legal(x, y)) addEdge(pos(i, j), pos(x, y), inf); } } else addEdge(pos(i, j), T, ans - num[i][j]); } return sum == maxFlow(); } inline lnt calc(lnt ans) { lnt ret = 0; for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) ret += ans - num[i][j]; return ret >> 1; } inline lnt maxNum(void) { lnt ret = 0; for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) if (ret < num[i][j]) ret = num[i][j]; return ret; } signed main(void) { for (int cas = nextInt(); cas--; ) { n = nextInt(); m = nextInt(); for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) num[i][j] = nextInt(); int cnt0 = 0, cnt1 = 0; lnt sum0 = 0, sum1 = 0; for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) if ((i ^ j) & 1) ++cnt1, sum1 += num[i][j]; else ++cnt0, sum0 += num[i][j]; if (cnt0 == cnt1) { if (sum0 != sum1) puts("-1"); else { lnt lt = maxNum(), rt = 1LL << 50, mid, ans = -1; while (lt <= rt) { mid = (lt + rt) >> 1; if (check(mid)) rt = mid - 1, ans = mid; else lt = mid + 1; } if (~ans) printf("%lld\n", calc(ans)); else puts("-1"); } } else { lnt ans = (sum0 - sum1) / (cnt0 - cnt1); if (ans < maxNum() || !check(ans)) puts("-1"); else printf("%lld\n", calc(ans)); } } }
@Author: YouSiki
相关文章推荐
- bzoj2756 [SCOI2012]奇怪的游戏 结论+网络流
- bzoj2756 [SCOI2012]奇怪的游戏
- 【BZOJ 2756】[SCOI2012]奇怪的游戏 网络流+二分
- BZOJ 2756: [SCOI2012]奇怪的游戏 [最大流 二分]
- BZOJ 2756: [SCOI2012]奇怪的游戏 网络流/二分
- 【二分+最大流】[SCOI2012]奇怪的游戏 BZOJ2756
- BZOJ 2756 [SCOI2012]奇怪的游戏
- bzoj 2756: [SCOI2012]奇怪的游戏(网络流+二分)
- BZOJ 2756 [SCOI2012]奇怪的游戏
- BZOJ 2756 SCOI 2012 奇怪的游戏 二分+最大流
- 【BZOJ】【2756】【SCOI2012】奇怪的游戏
- BZOJ 2756 [SCOI2012]奇怪的游戏 二分+网络流
- bzoj2756 [SCOI2012]奇怪的游戏
- bzoj2756: [SCOI2012]奇怪的游戏 二分+最大流
- 【bzoj2756】【SCOI2012】【奇怪的游戏】【最大流+二分】
- bzoj 2756: [SCOI2012]奇怪的游戏
- bzoj2756 [SCOI2012]奇怪的游戏
- Dinic最大流(bzoj 2756: [SCOI2012]奇怪的游戏)
- 【SCOI2012】bzoj2756 奇怪的游戏
- bzoj 2756 [SCOI2012]奇怪的游戏【二分+最大流】