Description

Blinker 最近喜欢上一个奇怪的游戏.

这个游戏在一个 \(n \times 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

Data Range

对于 30% 的数据, 保证 \(T \leq 10, 1 \leq n, m \leq 8\)

对于 100% 的数据, 保证 \(T \leq 10, 1 \leq n, m \leq 40\), 所有数为正整数且小于 \(10^9\).

Explanation

按照黄学长的思路来捋一遍想法:

首先遇到棋盘问题我们需要先给转化成黑白染色的棋盘 (他是这么想的, 但是我没有想到, 所以根本做不出来), 然后统计黑色的和白色的格子各有多少个、和是多少.

记黑色和白色的格子的数量和权值和分别为 \(cnt_0, cnt_1, sum_0, sum_1\).

设最后所有数都会变成 \(x\), 则:

\[cnt_1 \cdot x – sum_1 = cnt_2 \cdot x – sum_2\]

\[x = \frac{sum_1 - sum_2}{cnt_1 - cnt_2}\]

\(cnt_1 \neq cnt_2\) 时, 可以直接解出 \(x\) 然后直接判断可能性就可以了.

\(cnt_1 = cnt_2\) 时, 如果 \(sum_1 \neq sum_2\) 这个根本无法解出来; 不然我们会发现当某种情况合适时,\(\forall k \gt x\),\(k\) 都是满足条件的, 所以这时候二分搜索然后每一次用网络流判定就可以了.

关于网络流的问题, 详见代码, 建图方法和其他最小割问题相似.

Source Code


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cstring>

#define rep(_var,_begin,_end) for(int _var=_begin;_var<=_end;_var++)
using namespace std;
typedef long long lli;
const int maxN = 45, maxn = 2010, maxm = 10010;
const lli infinit = 0x007f7f7f7f7f7f7fll;
#define BLACK 0
#define WHITE 1

class Dinic
{
public:
    struct edge {
        int u, v;
        lli flow;
        edge *next, *rev;
    };
    int n, s, t, ecnt;
    edge *edges[maxn], epool[maxm];
    void add_edge(int u, int v, lli flow)
    {
        edge *p = &epool[++ecnt],
             *q = &epool[++ecnt];
        p->u = u; p->v = v; p->flow = flow;
        p->next = edges[u]; edges[u] = p;
        q->u = v; q->v = u; q->flow = 0;
        q->next = edges[v]; edges[v] = q;
        p->rev = q; q->rev = p;
        return ;
    }
    int level[maxn];
    bool make_level(void)
    {
        memset(level, 0, sizeof(level));
        queue<int> que;
        que.push(s);
        level[s] = 1;
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (edge *ep = edges[p]; ep; ep = ep->next)
                if (ep->flow && !level[ep->v]) {
                    level[ep->v] = level[p] + 1;
                    que.push(ep->v);
                }
        }
        if (level[t] <= 0)
            return false;
        return true;
    }
    lli dfs(int p, lli mn)
    {
        if (p == t)
            return mn;
        lli sum = 0, tmp;
        for (edge *ep = edges[p]; ep && sum < mn; ep = ep->next)
            if (ep->flow && level[ep->v] == level[p] + 1) {
                tmp = dfs(ep->v, min(ep->flow, mn - sum));
                if (tmp > 0) {
                    sum += tmp;
                    ep->flow -= tmp;
                    ep->rev->flow += tmp;
                }
            }
        if (sum < mn)
            level[p] = 0;
        return sum;
    }
    lli dinic(void)
    {
        lli sum = 0, tmp;
        while (make_level()) {
            tmp = dfs(s, infinit);
            if (tmp <= 0)
                break;
            sum += tmp;
        }
        return sum;
    }
    void clear(void)
    {
        n = s = t = 0;
        ecnt = 0;
        memset(edges, 0, sizeof(edges));
        memset(epool, 0, sizeof(epool));
        memset(level, 0, sizeof(level));
        return ;
    }
} graph;

int T, n, m;
int arr[maxN][maxN],
    color[maxN][maxN];

const int movx[4] = { 0, 0, 1, -1 },
          movy[4] = { 1, -1, 0, 0 };
bool judge(lli target)
{
    lli sum = 0;
    graph.clear();
    graph.n = n*m + 2;
    graph.s = graph.n - 1;
    graph.t = graph.n;
    #define pos(__x,__y) (((__x)-1)*m+(__y))
    rep(i, 1, n) rep(j, 1, m) {
        if (color[i][j]) {
            graph.add_edge(graph.s, pos(i, j), target - arr[i][j]);
            sum += target - arr[i][j];
            rep(k, 0, 3) {
                int nx = i + movx[k], ny = j + movy[k];
                if (nx < 1 || nx > n || ny < 1 || ny > m)
                    continue;
                graph.add_edge(pos(i, j), pos(nx, ny), infinit);
            }
        } else {
            graph.add_edge(pos(i, j), graph.t, target - arr[i][j]);
        }
    }
    if (graph.dinic() >= sum)
        return true;
    return false;
}

int main(int argc, char** argv)
{
    scanf("%d", &T);
    for (int idx = 1; idx <= T; idx++) {
        scanf("%d%d", &n, &m);
        int vmx = 0;
        rep(i, 1, n) rep(j, 1, m) {
            scanf("%d", &arr[i][j]);
            color[i][j] = (i+j) & 1;
            vmx = max(vmx, arr[i][j]);
        }
        // Stating all colours' sum and cnt
        int cnt[2] = { 0, 0 };
        lli sum[2] = { 0, 0 };
        rep(i, 1, n) rep(j, 1, m) {
            sum[color[i][j]] += arr[i][j];
            cnt[color[i][j]] += 1;
        }
        // Multiple cases.
        if (cnt[0] != cnt[1]) {
            lli res = (sum[0] - sum[1]) / (cnt[0] - cnt[1]);
            if (res >= vmx && judge(res))
                printf("%lld\n", res * cnt[1] - sum[1]);
            else
                printf("-1\n");
        } else if (cnt[0] == cnt[1]) {
            if (sum[0] != sum[1]) {
                printf("-1\n");
            } else if (sum[0] == sum[1]) {
                lli l = vmx, r = infinit;
                while (l <= r) {
                    lli mid = (l + r) / 2;
                    if (judge(mid))
                        r = mid - 1;
                    else
                        l = mid + 1;
                }
                printf("%lld\n", l * cnt[1] - sum[1]);
            }
        }
        continue;
    }
    return 0;
}