您的位置:首页 > 其它

BZOJ 2756: [SCOI2012]奇怪的游戏

2017-01-22 10:17 387 查看

2756: [SCOI2012]奇怪的游戏

Time Limit: 40 Sec Memory Limit: 128 MB
Submit: 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

2

2 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: