• BZOJ2756 [SCOI2012]奇怪的游戏 最大流


    好久没有写博客了。不过这个博客也没有多少人看

    最近在写网络流,为了加深理解,来写一两篇题解。

    对整个棋盘进行黑白染色以后可以发现,一次操作就是让二分图的两个点的值分别 (+1)

    这样,我们就可以对一个答案的合法性做出判断了。

    对于每个白点,从 (S) 向它连 (ans - a[i][j]) 的边。黑点向 (T)(ans - a[i][j]) 的边。每个白点向黑点建 (+infty) 的边。

    如果满流就成立。

    发现答案满足单调性,然后就开始非常开心地二分答案?

    你会发现从 (S) 出来的容量和与流向 (T) 的容量和根本不一定等。

    但是它们必须等。

    于是我们设 (cnt_1) 为白点的数量,(sum_1) 为白点初始权值之和。(cnt_0, sum_0) 为黑点的。

    那么必须有:

    [ans cdot cnt_0 - sum_0 = ans cdot cnt_1 - sum_1 ]

    可以化成

    [(cnt_o - cnt_1) cdot ans = sum_0 - sum_1 ]

    如果 (cnt_0 eq 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
    ", y, T);
    			if (y == T) return 1;
    		}
    	}
    	return 0;
    }
    inline ll dfs(int x, ll a) {
    	// dbg("x = %d a = %lld
    ", 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
    ", 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
    ", l);
    			// for (ll i = l - 10000; i <= l + 10000; ++i)
    				// dbg("When i = %lld, chk = %d
    ", i, (int)check(i));
    			if (l < INF) printf("%lld
    ", 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
    ", 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);
    }
    
  • 相关阅读:
    Scrum与看板区别
    Android中的Apk的加固(加壳)原理解析和实现
    规模化敏捷开发的10个最佳实践
    TDD、BDD、ATDD、DDD 软件开发模式
    如何解决秒杀的性能问题和超卖的讨论
    mongo数据库的各种查询语句示例
    linux if -d -e -f表达的意思
    prometeus, grafana部署以及监控mysql
    2019年目标
    nginx 动态添加ssl模块
  • 原文地址:https://www.cnblogs.com/hankeke/p/10699010.html
Copyright © 2020-2023  润新知