[CQOI2017]老C的方块

题意:

老C是个程序员。
游戏被限定在一个由小方格排成的R行C列网格上,有些相邻的小方格之间的公共边比较特殊。
特殊的公共边排列得有很强的规律,下图所示是一个R=C=8的网格,蓝色标注的边是特殊边。
如果网格的规模更大,我们可以用同样的方法找出所有的特殊边。
网格的每个小方格刚好可以放入一个小方块,在游戏的一开始,有些小方格已经放上了小方块,另外的小方格没有放。
有一些小方块排列成了它讨厌的形状(特殊边的位置也要如图中所示),即使是经过任意次旋转、翻转。
为了防止弃疗,老C决定移除一些格子里小方块,使得剩下的小方块不能构成它讨厌的形状。
但是游戏里每移除一个方块都是要花费一些金币的,老C当然希望尽可能少的使用游戏里的金币。

题解:

网络流。
首先,观察题目中的图

可以发现,蓝边周围的两个点,要么破坏一个,要么破坏周围的。而周围的都满足距离为奇数。
看到这个条件,又是网格图,可以黑白染色。
这样,周围的点的颜色一定不同,可以建二分图,使用最小割。
然而,有一个“要么破坏一个,要么破坏周围的。”的要求,即“或”的限制。
在最小割中,“或”就是串联,这个割掉一个就破坏了路径。
而周围的破坏所有才行,即“与”,就是并联。
把中间两个点连min的边,周围的点并联,再串联到中间的点上。
如图

最小割即可,跑不满,所以能过。
注意建图细节。

代码:

#include <stdio.h> 
#include <map> 
using namespace std;
#define N 200010
#define M 1000010
#define min(a, b) a < b ? a: b
#define ll long long 
int fr[N],ne[M],v[M],w[M],bs = 0;
void add(int a, int b, int c) {
	v[bs] = b;
	w[bs] = c;
	ne[bs] = fr[a];
	fr[a] = bs++;
}
int dl[N],jl[N],dy[N],S,T,n,inf = 2000000000;
bool bk[N];
bool bfs() {
	for (int i = 1; i <= n; i++) {
		bk[i] = false;
		jl[i] = inf;
	}
	int he = 0,	ta = 1;
	dl[he] = S;
	bk[S] = true;
	jl[S] = 0;
	while (he < ta) {
		int u = dl[he];
		for (int i = fr[u]; i != -1; i = ne[i]) {
			if (w[i] > 0 && !bk[v[i]]) {
				bk[v[i]] = true;
				jl[v[i]] = jl[u] + 1;
				dl[ta++] = v[i];
			}
		}
		he += 1;
	}
	return jl[T] < inf;
}
int dfs(int u, int z) {
	if (u == T) return z;
	for (int & i = dy[u]; i != -1; i = ne[i]) {
		if (w[i] > 0 && jl[v[i]] == jl[u] + 1) {
			int rt = dfs(v[i], min(w[i], z));
			if (rt != -1) {
				w[i] -= rt;
				w[i ^ 1] += rt;
				return rt;
			}
		}
	}
	return - 1;
}
int dinic() {
	int jg = 0;
	while (bfs()) {
		for (int i = 1; i <= n; i++) dy[i] = fr[i];
		while (1) {
			int rt = dfs(S, inf);
			if (rt == -1) break;
			jg += rt;
		}
	}
	return jg;
}
void addb(int a, int b, int c) {
	add(a, b, c);
	add(b, a, 0);
}
int x[100010],y[100010],z[100010],fx[6][2] = {-1,0,1,0,0,-1,-1,1,1,1,0,2},r,c;
map < ll,int > mp;
bool check(int x, int y) {
	if (x % 2 == 1) return y % 4 == 1;
	else return y % 4 == 3;
}
int getwz(int x, int y) {
	if (x <= 0 || x > r || y <= 0 || y > c) return - 1;
	ll t = 1ll * x * c + y;
	if (mp.count(t) == 0) return - 1;
	return mp[t];
}
void link(int z1, int z2, int a, int b) {
	addb(a, b, min(z[z1], z[z2]));
	for (int s = 0; s < 6; s++) {
		int tx = x[z1] + fx[s][0],
		ty = y[z1] + fx[s][1];
		int wz = getwz(tx, ty);
		if (wz == -1) continue;
		if ((tx + ty) % 2 == 0) addb(wz + 3, a, inf);
		else addb(b, wz + 3, inf);
	}
}
int main() {
	int m;
	scanf("%d%d%d", &c, &r, &m);
	for (int i = 0; i < m; i++) scanf("%d%d%d", &y[i], &x[i], &z[i]);
	S = 1;	T = 2;	n = m + 2;
	for (int i = 1; i <= n; i++) fr[i] = -1;
	for (int i = 0; i < m; i++) {
		if ((x[i] + y[i]) % 2 == 0) addb(S, i + 3, z[i]);
		else addb(i + 3, T, z[i]);
		mp[1ll * x[i] * c + y[i]] = i;
	}
	for (int i = 0; i < m; i++) {
		int t;
		if (check(x[i], y[i]) && (t = getwz(x[i], y[i] + 1)) != -1) {
			fr[n + 1] = fr[n + 2] = -1;
			link(i, t, n + 1, n + 2);
			n += 2;
		}
	}
	printf("%d", dinic());
	return 0;
}
posted @ 2019-08-16 21:58  lnzwz  阅读(157)  评论(0编辑  收藏  举报