zoukankan      html  css  js  c++  java
  • P5038 [SCOI2012]奇怪的游戏 二分+网络流

    $ color{#0066ff}{ 题目描述 }$

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

    这个游戏在一个 (N imes M) 的棋盘上玩,每个格子有一个数。每次(Blinker)会选择两个相邻的格子,并使这两个数都加上(1)

    现在(Blinker)想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出(-1)

    (color{#0066ff}{输入格式})

    输入的第一行是一个整数(T),表示输入数据有T轮游戏组成。

    每轮游戏的第一行有两个整数(N)(M), 分别代表棋盘的行数和列数。 接下来有(N)行,每行(M)个数。

    (color{#0066ff}{输出格式})

    对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出(-1)

    (color{#0066ff}{输入样例})

    2 
    2 2 
    1 2 
    2 3 
    3 3 
    1 2 3 
    2 3 4 
    4 3 2 
    

    (color{#0066ff}{输出样例})

    2 
    -1 
    

    (color{#0066ff}{数据范围与提示})

    对于(30%)的数据,保证(T<=10,1<=N,M<=8)

    对于(100%)的数据,保证 (T<=10,1<=N,M<=40),所有数为正整数且小于(1000000000)

    (color{#0066ff}{题解})

    对于这种方格图,而且还是相邻的数操作,显然可以考虑黑白染色!

    可以发现,每次操作只会同时修改两个颜色不同的位置(显然。。。)

    既然最后所有的数相同,不妨假设那个数是x,那么再结合黑白染色,可以得出一个式子(有解的情况下)

    (黑点数量*x-黑点权值和=白点数量*x-白点权值和),其实就是操作次数相同的等式

    观察一下这个式子。。。嗯????xTM能解出来我去,不会就这样水了吧。。。

    当然不会,x能解出来当且仅当黑白点数量不同(除数不为0),这样我们就能特判一下得到x

    当然x可能不合法,要判一下,判断的方法下面会说

    那么接下来就是黑白点相同的情况了

    根据上面的式子,这时候如果两个权值和不等,那显然无解

    那如果相等呢??如何求解x?

    考虑二分答案,但是我们要判断有没有单调性

    显然当前的图肯定是一个有偶数点个数的图,如果当前x成立,显然由于图是偶数个点,x+1一定是成立的

    反之同理,于是这是有单调性的

    然后,二分完x,我们如何判断x是否合法(都黑白染色了当然是网络流)

    起点向每个白点连x-权值的边,代表需要几次操作,中间连inf的边,右边同理

    当前方案合法当且仅当最大流可以满足所有点, 这也是上面判断x是否合法的方式

    #include<bits/stdc++.h>
    #define LL long long
    LL read() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    template<class T> bool chkmax(T &a, const T &b) { return a < b? a = b, 1 : 0; }
    template<class T> bool chkmin(T &a, const T &b) { return b < a? a = b, 1 : 0; }
    const int maxn = 5e5 + 10;
    const LL inf = 9999999999999LL;
    struct node {
    	LL to, can;
    	node *nxt, *rev;
    	node(LL to = 0, LL can = 0, node *nxt = NULL): to(to), can(can), nxt(nxt) { rev = NULL; }
    }pool[maxn], *tail, *head[maxn], *cur[maxn];
    int dep[maxn], n, m, s, t, mp[66][66];
    int rx[] = {1, -1, 0, 0};
    int ry[] = {0, 0, 1, -1};
    void add(int from, int to, LL can) { head[from] = new(tail++) node(to, can, head[from]); }
    void link(int from, int to, LL can) {
    	add(from, to, can), add(to, from, 0LL);
    	head[from]->rev = head[to]; head[to]->rev = head[from];
    }
    bool bfs() {
    	for(int i = s; i <= t; i++) dep[i] = 0, cur[i] = head[i];
    	std::queue<int> q; 
    	q.push(s); dep[s] = 1;
    	while(!q.empty()) {
    		int tp = q.front(); q.pop();
    		for(node *i = head[tp]; i; i = i->nxt) 
    			if(!dep[i->to] && i->can)
    				dep[i->to] = dep[tp] + 1, q.push(i->to); 
    	}
    	return dep[t];
    }
    LL dfs(int x, LL change) {
    	if(x == t || !change) return change;
    	LL flow = 0, ls;
    	for(node *&i = cur[x]; i; i = i->nxt)
    		if(dep[i->to] == dep[x] + 1 && (ls = dfs(i->to, std::min(i->can, change)))) {
    			flow += ls;
    			change -= ls;
    			i->can -= ls;
    			i->rev->can += ls;
    			if(!change) break;
    		}
    	return flow;
    }
    LL dinic() {
    	LL flow = 0;
    	while(bfs()) flow += dfs(s, inf);
    	return flow;
    }
    int id(int x, int y) { return (x - 1) * m + y; }
    bool ok(LL mid) {
    	tail = pool;
    	s = 0, t = n * m + 1;
    	for(int i = s; i <= t; i++) head[i] = NULL;
    	LL now = 0;
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= m; j++) {
    			if((i + j) & 1) {
    				now += mid - mp[i][j], link(s, id(i, j), mid - mp[i][j]);
    				for(int k = 0; k < 4; k++) {
    					int xx = i + rx[k];
    					int yy = j + ry[k];
    					if(xx >= 1 && xx <= n && yy >= 1 && yy <= m) link(id(i, j), id(xx, yy), inf);
    				}
    			}
    			else link(id(i, j), t, mid - mp[i][j]);
    		}
    	return now == dinic();
    }
    
    int main() {
    	for(int T = read(); T --> 0;) {
    		n = read(), m = read();
    		int maxval = 0;
    		for(int i = 1; i <= n; i++)
    			for(int j = 1; j <= m; j++)
    				chkmax(maxval, mp[i][j] = read());
    		LL tot0 = 0, tot1 = 0, num0 = 0, num1 = 0;
    		for(int i = 1; i <= n; i++)
    			for(int j = 1; j <= m; j++) {
    				if((i + j) & 1) num1++, tot1 += mp[i][j];
    				else num0++, tot0 += mp[i][j];
    			}
    		if(num0 ^ num1) {
    			LL endval = (tot0 - tot1) / (num0 - num1);
    			if(endval >= maxval && ok(endval)) printf("%lld
    ", endval * num1 - tot1);
    			else puts("-1");
    		}
    		else {
    			if(tot0 ^ tot1) puts("-1");
    			else {
    				LL l = maxval, r = inf >> 1, ans = r;
    				while(l <= r) {
    					LL mid = (l + r) >> 1;
    					if(ok(mid)) ans = mid, r = mid - 1;
    					else l = mid + 1;
    				}
    				printf("%lld
    ", ans == inf >> 1? -1 : ans * num1 - tot1);
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    vue-cli工具搭建vue-webpack项目
    关于闭包的理解
    运动-分页
    运动-无缝滚动
    运动-手风琴
    运动-模拟返回顶部
    运动—图片中心放大
    运动—运动框架
    webstorm 激活破解
    let和const
  • 原文地址:https://www.cnblogs.com/olinr/p/10644809.html
Copyright © 2011-2022 走看看