BZOJ2756 [SCOI2012]奇怪的游戏 最大流
好久没有写博客了。不过这个博客也没有多少人看
最近在写网络流,为了加深理解,来写一两篇题解。
对整个棋盘进行黑白染色以后可以发现,一次操作就是让二分图的两个点的值分别 \(+1\)。
这样,我们就可以对一个答案的合法性做出判断了。
对于每个白点,从 \(S\) 向它连 \(ans - a[i][j]\) 的边。黑点向 \(T\) 连 \(ans - a[i][j]\) 的边。每个白点向黑点建 \(+\infty\) 的边。
如果满流就成立。
发现答案满足单调性,然后就开始非常开心地二分答案?
你会发现从 \(S\) 出来的容量和与流向 \(T\) 的容量和根本不一定等。
但是它们必须等。
于是我们设 \(cnt_1\) 为白点的数量,\(sum_1\) 为白点初始权值之和。\(cnt_0, sum_0\) 为黑点的。
那么必须有:
\]
可以化成
\]
如果 \(cnt_0 \neq cnt_1\) 可以直接把 \(ans\) 算出来验证一下就可以了。
如果 \(cnt_o = cnt_1\) 那么就是说 \(sum_o = sum_1\) 不满足就可以直接Skipped
了。如果满足的话 \(ans\) 可以随便取值,那么就可以二分求出最小的了。
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char SMAX(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char SMIN(A &a, const B &b) {return b < a ? a = b , 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I>
inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int MAXN = 40 + 7;
const int N = MAXN * MAXN;
const int M = MAXN * MAXN * 3;
const int dx[] = {0, 1, 0, -1};
const int dy[] = {1, 0, -1, 0};
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int INF_int = 0x3f3f3f3f;
int n, m, S, T, nod, cnt0, cnt1, hd, tl, allsize;
ll sum0, sum1, sum;
int a[MAXN][MAXN], id[MAXN][MAXN], col[MAXN][MAXN];
int q[N], cur[N], dis[N];
struct Edge {int to, ne; ll f;} g[M << 1]; int head[N], tot = 1;
inline void addedge(int x, int y, ll z) {g[++tot].to = y; g[tot].f = z; g[tot].ne = head[x]; head[x] = tot;}
inline void adde(int x, int y, ll z) {addedge(x, y, z); addedge(y, x, 0);}
inline bool bfs() {
memset(dis, 0x3f, allsize); memcpy(cur, head, allsize);
q[hd = 0, tl = 1] = S; dis[S] = 0;
while (hd < tl) {
int x = q[++hd];
for fec(i, x, y) if (g[i].f && dis[y] == INF_int) {
dis[y] = dis[x] + 1; q[++tl] = y;
// dbg("y = %d, T = %d\n", y, T);
if (y == T) return 1;
}
}
return 0;
}
inline ll dfs(int x, ll a) {
// dbg("x = %d a = %lld\n", x, a);
if (x == T || !a) return a;
ll flow = 0, f;
for (int &i = cur[x]; i; i = g[i].ne) {
int y = g[i].to;
if (dis[y] != dis[x] + 1) continue;
if (!(f = dfs(y, std::min(a, g[i].f)))) continue;
g[i].f -= f, g[i ^ 1].f += f;
flow += f, a -= f; if (!a) break;
}
if (!flow) dis[x] = INF_int;
return flow;
}
inline ll dinic() {
ll ans = 0;
while (bfs()) ans += dfs(S, INF);
// dbg("ans = %lld\n", ans);
return ans;
}
inline bool build(ll mid) {
memset(head, 0, sizeof(head));
tot = 1; sum = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
if (mid < a[i][j]) return 0;
if (col[i][j]) {
adde(S, id[i][j], mid - a[i][j]);
sum += mid - a[i][j];
for (int k = 0; k < 4; ++k) {
int px = i + dx[k], py = j + dy[k];
if (px < 1 || px > n || py < 1 || py > m) continue;
adde(id[i][j], id[px][py], INF);
}
} else adde(id[i][j], T, mid - a[i][j]);
}
return 1;
}
inline bool check(ll mid) {
if (!build(mid)) return 0;
return dinic() == sum;
}
inline void work() {
if (cnt0 == cnt1) {
if (sum0 != sum1) {
puts("-1");
return;
} else {
ll l = 0, r = INF;
while (l < r) {
ll mid = (l + r) >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
// dbg("l = %d\n", l);
// for (ll i = l - 10000; i <= l + 10000; ++i)
// dbg("When i = %lld, chk = %d\n", i, (int)check(i));
if (l < INF) printf("%lld\n", l * cnt0 - sum0);
else puts("-1");
}
} else {
ll dsum = sum0 - sum1;
int dcnt = cnt0 - cnt1;
if (((dsum < 0 && dcnt < 0) || (dsum > 0 && dcnt > 0)) && dsum % dcnt == 0) {
ll ans = dsum / dcnt;
if (check(ans)) printf("%lld\n", ans * cnt0 - sum0);
else puts("-1");
}
}
}
inline void cls() {
sum0 = sum1 = 0;
cnt0 = cnt1 = 0;
}
inline void init() {
cls();
read(n), read(m);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
read(a[i][j]);
col[i][j] = (i + j) & 1;
if (col[i][j]) ++cnt1, sum1 += a[i][j];
else ++cnt0, sum0 += a[i][j];
if (j > 1) id[i][j] = id[i][j - 1] + 1;
else id[i][j] = id[i - 1][m] + 1;
}
}
S = id[n][m] + 1, T = nod = id[n][m] + 2;
allsize = (nod + 1) * sizeof(int);
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
int T; read(T);
while (T--) {
init();
work();
}
fclose(stdin);
}
BZOJ2756 [SCOI2012]奇怪的游戏 最大流的更多相关文章
- BZOJ2756:[SCOI2012]奇怪的游戏(最大流,二分)
Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻 的格子,并使这两个数都加上 1. 现在 B ...
- 【BZOJ-2756】奇怪的游戏 最大流 + 分类讨论 + 二分
2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 2925 Solved: 792[Submit][Stat ...
- Bzoj2756 [SCOI2012]奇怪的游戏
2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 3220 Solved: 886 Description ...
- bzoj2756: [SCOI2012]奇怪的游戏(网络流+分情况)
2756: [SCOI2012]奇怪的游戏 题目:传送门 题解: 发现做不出来的大难题一点一个网络流 %大佬 首先黑白染色(原来是套路...)染色之后就可以保证每次操作都一定会使黑白各一个各自的值加1 ...
- BZOJ 2756: [SCOI2012]奇怪的游戏 [最大流 二分]
2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 3352 Solved: 919[Submit][Stat ...
- BZOJ 2756 SCOI2012 奇怪的游戏 最大流
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2756 Description Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N ...
- BZOJ2756 [SCOI2012]奇怪的游戏 【网络流 + 二分】
题目 Blinker最近喜欢上一个奇怪的游戏. 这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数.每次 Blinker 会选择两个相邻 的格子,并使这两个数都加上 1. 现在 Blinker 想知 ...
- BZOJ2756 SCOI2012奇怪的游戏(二分答案+最大流)
由数据范围容易想到网络流.由于操作只是对于棋盘上相邻两格,容易想到给其黑白染色. 假设已经知道最后要变成什么数.那么给黑白点之间连边,其流量则表示同时增加的次数,再用源汇给其限流为需要增加的数即可. ...
- BZOJ 2756: [SCOI2012]奇怪的游戏 网络流/二分
2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 1594 Solved: 396[Submit][Stat ...
随机推荐
- 【HDOJ6667】Roundgod and Milk Tea(模拟)
题意:有n个班级,每个班级有a[i]个人,b[i]杯奶茶 每个人至多喝一杯奶茶,且不能喝自己班的 问能喝到奶茶的最多总人数 n<=1e6,a[i],b[i]<=1e9 思路: 做法一: # ...
- Nexus Repository OSS 3安装配置使用
Nexus Repository OSS 3是一个开源的仓库管理系统,提供了更加丰富的功能,而且安装.配置.使用起来也更加简单方便.OSS 3版本主要支持的仓库(Repository)包括如下: bo ...
- [CSP-S模拟测试]:chess(搜索+最短路)
题目描述 $pig$在下象棋的时候特别喜欢用马,他总是计算着自己的马还需要几步才能吃掉对方的帅,以及方案数的个数,当然$pig$很笨,所以他只能求助于你.我们假设在$n\times m$的棋盘上,$p ...
- java 将数据库中的每一条数据取出放入数组或者List中
1.如何将数据库中数据按照行(即一整条数据)取出来,存入到数组当中? public static String str = null; // 将StringBuffer转化成字符串 public st ...
- Flueme学习之路(一)Flume的基础介绍
背景 Hadoop业务的整体开发流程: 从Hadoop的业务开发流程中可以看出,在大数据的业务处理流程中,对于数据的采集是十分重要的一步,也是不可避免的一步. 许多公司的平台每天会产生大量的日 ...
- JavaScript 获取随机整数
Math.random()方法会返回介于 0(包含) ~ 1(不包含) 之间的一个随机数 假如想要拿到0-10之间的数,只需要将该方法的值*10 即Math.random()*10: 假如想要拿到0- ...
- C语言的未初始化的数组的值为什么是随机的
突然想起来前几天同学问我为什么没有初始化的数组的值是随机的,发现这个困惑自己也是存在的,所以自己总结的心得. 1. 首先,并不是所有未初始化的数组的值都是随机的.对于没有初始化的数组,分两种情况: ( ...
- Delphi中Tlist实例
http://blog.163.com/jiandande3218@126/blog/static/74728469201132721428194/ Delphi中Tlist实例 2011-04-27 ...
- c# 调用 webService
开局几张 照着做就完事 说明下 这个wsdl 文件是根据别人提供的webService 接口 打开后改变后缀来的 这样就引用完成了 接下来就是重点了 怎么调用 localhost.WsSyncDu ...
- 跨平台自动构建工具v1.0.2 发布
XMake是一个跨平台自动构建工具,支持在各种主流平台上构建项目,类似cmake.automake.premake,但是更加的方便易用,工程描述语法更简洁直观,支持平台更多,并且集创建.配置.编译.打 ...