[BZOJ4823][CQOI2017]老C的方块

bzoj
luogu

Description

老C是个程序员。
作为一个懒惰的程序员,老C经常在电脑上玩方块游戏消磨时间。游戏被限定在一个由小方格排成的R行C列网格上,如果两个小方格有公共的边,就称它们是相邻的,而且有些相邻的小方格之间的公共边比较特殊。特殊的公共边排列得有很强的规律。首先规定,第1行的前两个小方格之间的边是特殊边。然后,特殊边在水平方向上每4个小方格为一个周期,在竖直方向上每2个小方格为一个周期。所有的奇数列与下一列之间都有特殊边,且所在行的编号从左到右奇偶交替。
下图所示是一个R=C=8的网格,蓝色标注的边是特殊边。首先,在第1行,第1列和第2列之间有一条特殊边。因为竖直方向周期为2,所以所有的奇数行,第1列和第2列之间都有特殊边。因为水平方向周期为4,所以所有奇数行的第5列和第6列之间也有特殊边,如果网格足够大,所有奇数行的第9列和第10列、第13列和第14列之间都有特殊边。因为所有的奇数列和下一列之间都有特殊边,所以第3列和第4列、第7列和第8列之间也有特殊边,而所在行的编号从左到右奇偶交替,所以它们的特殊边在偶数行。如果网格的规模更大,我们可以用同样的方法找出所有的特殊边。

网格的每个小方格刚好可以放入一个小方块,在游戏的一开始,有些小方格已经放上了小方块,另外的小方格没有放。老C很讨厌下图所示的图形,如果他发现有一些小方块排列成了它讨厌的形状(特殊边的位置也要如图中所示),就很容易弃疗,即使是经过任意次旋转、翻转后排列成讨厌的形状,老C也同样容易弃疗。

为了防止弃疗,老C决定趁自己还没有弃疗,赶紧移除一些格子里小方块,使得剩下的小方块不能构成它讨厌的形状。但是游戏里每移除一个方块都是要花费一些金币的,每个方块需要花费的金币有多有少参差不齐。老C当然希望尽可能少的使用游戏里的金币,但是最少要花费多少金币呢?老C懒得思考,就把这个问题交给你了。

Input

第一行有3个正整数C,R,n,表示C列R行的网格中,有n个小方格放了小方块。接下来n行,每行3个正整数x,y,w,表示在第x列第y行的小方格里放了小方块,移除它

需要花费w个金币。保证不会重复,且都在网格范围内。

Output

输出一行,包含一个整数,表示最少花费的金币数量。

Sample Input 1

2 2 4
1 1 5
1 2 6
2 1 7
2 2 8

Sample Output 1

5

Sample Input 2

3 3 7
1 1 10
1 2 15
1 3 10
2 1 10
2 2 10
2 3 10
3 1 10

Sample Output 2

15

sol

这种网格图尝试一下黑白染色?
染完之后发现这个老C会弃疗的图案没有太大关系啊。
怎么办呢?我们按照如下方式染色。
别问我是怎么知道这么染色的
这样以来,所有会让老C弃疗的图案都会是一条红-黄-蓝-绿的连续路径
也就是说我们要消灭所有的这种连续路径。
考虑最小割,按如下方式建图即可。

坐标范围有点大,可以直接开\(map\)存。
至于复杂度...不会证qaq

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 1e5+5;
const int inf = 2e9;
struct edge{int to,nxt,w;}a[N<<4];
int n,x[N],y[N],val[N],col[N],S,T,head[N],cnt=1,dep[N],cur[N];
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
map<int,int>M[N];queue<int>Q;
void link(int u,int v,int w){
	a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
	a[++cnt]=(edge){u,head[v],0};head[v]=cnt;
}
bool bfs(){
	memset(dep,0,sizeof(dep));
	dep[S]=1;Q.push(S);
	while (!Q.empty()){
		int u=Q.front();Q.pop();
		for (int e=head[u];e;e=a[e].nxt)
			if (a[e].w&&!dep[a[e].to])
				dep[a[e].to]=dep[u]+1,Q.push(a[e].to);
	}
	return dep[T];
}
int dfs(int u,int f){
	if (u==T) return f;
	for (int &e=cur[u];e;e=a[e].nxt)
		if (a[e].w&&dep[a[e].to]==dep[u]+1){
			int tmp=dfs(a[e].to,min(a[e].w,f));
			if (tmp) {a[e].w-=tmp;a[e^1].w+=tmp;return tmp;}
		}
	return 0;
}
int dinic(){
	int res=0;
	while (bfs()){
		for (int i=1;i<=T;++i) cur[i]=head[i];
		while (int tmp=dfs(S,inf)) res+=tmp;
	}
	return res;
}
void build1(int v){
	for (int d=0;d<4;++d){
		int i=x[v]+dx[d],j=y[v]+dy[d],u=M[i][j];
		if (u)
			if (col[u]==2) link(v,u,min(val[u],val[v]));
			else link(u,v,inf);
	}
}
void build2(int u){
	for (int d=0;d<4;++d){
		int i=x[u]+dx[d],j=y[u]+dy[d],v=M[i][j];
		if (v)
			if (col[v]==1) link(u,v,min(val[u],val[v]));
			else link(u,v,inf);
	}
}
int main(){
	gi();gi();n=gi();S=n+1;T=S+1;
	for (int i=1;i<=n;++i){
		x[i]=gi(),y[i]=gi(),val[i]=gi();
		M[x[i]][y[i]]=i;
		col[i]=(x[i]%4>=2)*2+(((x[i]+y[i])&1)^1);
	}
	for (int i=1,j;i<=n;++i)
		if (col[i]==0) link(S,i,val[i]);
		else if (col[i]==1) build1(i);
		else if (col[i]==2) build2(i);
		else link(i,T,val[i]);
	printf("%d\n",dinic());return 0;
}
posted @ 2018-05-28 14:42  租酥雨  阅读(223)  评论(0编辑  收藏  举报