传送门http://www.lydsy.com/JudgeOnline/problem.php?id=2756
基本思路:我们考虑可以变成的最终的状态,假设每一位置的数都是x, 由于对于相连的格子一起加1, 那么我们就可以对方格进行黑白染色(相邻格子的颜色严格不同),假设个数分别为c1, c2, 总和为s1, s2, 那么 c1 * x - s1 = c2 * x - s2, x = (s1 - s2) / (c1 - c2); 当n * m % 2 == 1 时 c1 != c2, 可以反解x; 否则x在某一个值以上的时候一定都可以满足,就可以二分了。
重点就是如何检验x是否可行,然后方法是对于黑格子向原点连x - a[i][j] 的边,然后白格子向汇点连x - a[i][j]的边, 然后相邻的格子连inf的边,跑满流就可以了。
warning: 首先我们需要两个inf 分别表示流量的最大值和二分最大值,不能一样,然后二分的最小边界必须是a[i][j] 中的最大值减1,因为要保证x > Max{a[i][j]}
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long qword; const qword maxl = 50; const qword maxn = maxl * maxl; const qword dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; const qword inf = 0x3f3f3f3f; const qword infll = (1ll << 60); qword n, m, sum[2], c[2]; qword a[maxl][maxl], col[maxl][maxl]; struct edge { qword t, c; edge* next, *other; }e[maxn * 10]; qword ne = 0; edge* head[maxn]; void addedge(qword f, qword t, qword c) { e[ne].t = t, e[ne].c = c, e[ne].next = head[f], head[f] = e + ne ++; e[ne].t = f, e[ne].c = 0, e[ne].next = head[t], head[t] = e + ne ++; e[ne - 1].other = e + ne - 2; e[ne - 2].other = e + ne - 1; } qword q[maxn], l, r; qword dis[maxn]; qword s, t; bool bfs() { memset(dis, 0, sizeof(dis)); dis[s] = 1; l = r = 0; q[r ++] = s; while(l ^ r) { qword x = q[l ++]; for(edge* p = head[x]; p; p = p-> next) { if(!dis[p-> t] && p-> c) { q[r ++] = p-> t; dis[p-> t] = dis[x] + 1; } } } return (bool)dis[t]; } /*qword dfs(qword x, qword flow) { if(x == t) return flow; if(!flow) return 0; qword ret = 0, maxflow; for(edge* p = head[x]; p && flow; p = p-> next) { if(p-> c && dis[p-> t] == dis[x] + 1 && (maxflow = dfs(p-> t, min(flow, p-> c)))) { p-> c -= maxflow, p-> other-> c += maxflow; ret += maxflow, flow -= maxflow; } } return ret; }*/ edge* Next[maxn]; qword sta[maxn], top = 0; qword dfs() { for(qword i = s; i <= t; ++ i) Next[i] = head[i]; top = 0; sta[++ top] = s; qword ret = 0; while(top) { qword x = sta[top]; if(x == t) { qword minfl = infll + 1000; qword last; for(qword i = 1; i < top; ++ i) { if(Next[sta[i]]-> c < minfl) minfl = Next[sta[i]]-> c, last = i; } for(qword i = 1; i < top; ++ i) { edge* p = Next[sta[i]]; p-> c -= minfl, p-> other-> c += minfl; } ret += minfl, top = last; continue; } edge* px; for(px = Next[x]; px; px = px-> next) { if(dis[px-> t] == dis[x] + 1 && px-> c) { sta[++ top] = px-> t; break; } } Next[x] = px; if(Next[x] == NULL) { top --; if(Next[sta[top]]) Next[sta[top]] = Next[sta[top]]-> next; } } return ret; } qword get(qword x, qword y) { return (x - 1) * m + y; } bool flow(qword x) { ne = 0; memset(head, 0, sizeof(head)); qword tl = 0; for(qword i = 1; i <= n; ++ i) { for(qword j = 1; j <= m; ++ j) { qword tmp = x - a[i][j]; if(col[i][j] == 0) addedge(0, get(i, j), tmp); else addedge(get(i, j), t, tmp); tl += tmp; } } for(qword i = 1; i <= n; ++ i) { for(qword j = 1; j <= m; ++ j) { if(col[i][j] == 0) { for(qword l = 0; l < 4; ++ l) { qword x = i + dir[l][0]; qword y = j + dir[l][1]; if(x < 1 || x > n || y < 1 || y > m) continue; addedge(get(i, j), get(x, y), infll); } } } } qword fl = 0; while(bfs()) fl += dfs(); return (tl == fl * 2); } qword getans(qword x) { return ((n * m * x - sum[0] - sum[1]) >> 1); } void sov() { qword T; scanf("%lld", &T); qword Max = 0; while(T --) { Max = 0; scanf("%lld%lld", &n, &m); memset(sum, 0, sizeof(sum)); memset(c, 0, sizeof(c)); col[0][1] = 1; for(qword i = 1; i <= n; ++ i) { qword cl = !col[i - 1][1]; for(qword j = 1; j <= m; ++ j) { scanf("%lld", &a[i][j]); col[i][j] = cl; cl = !cl; c[col[i][j]] ++, sum[col[i][j]] += a[i][j]; Max = max(Max, a[i][j]); } } s = 0, t = n * m + 1; if(n * m % 2) { qword x = abs(sum[0] - sum[1]); if(x >= Max && flow(x)) printf("%lld\n", getans(x)); else printf("-1\n"); } else { qword ls = Max - 1, rs = inf * 1000; qword ans = -1; while(rs - ls > 1) { qword mid = (ls + rs) >> 1; if(flow(mid)) rs = mid, ans = mid; else ls = mid; } if(ans == -1) printf("-1\n"); else printf("%lld\n", getans(ans)); } } } int main() { //freopen("test.in", "r", stdin); //freopen("test.out", "w", stdout); sov(); return 0; }