【题解】[CQOI2017] 老C的方块

题目描述

在这里插入图片描述
在这里插入图片描述

sol:

其实是最小割的板题。

首先看到网格图,以及这个奇怪的形状,我们想到网络流。

为了赋予它实际意义,我们考虑染色。

这样就转化成了一条 1-2-3-4 的路径。

最后按照割点的方式做即可。

总结:本题虽然思维难度不大,但是建图方式是非常常见的,与 文理分科 不同,那道题有 选文 /理科 两种状况和对应贡献,所以要文边建左边,理边建右边;本题直接用分层图的思想来建就好了。

qwq

#include<bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=2e5+5;
const int M=1e7+5;
int r,c,n,s,t,rt,res,id[2][N];
int lab[N],que[N],cur[N];
int tot,head[M],nxt[M],to[M],cap[M];
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int col[4][4]={{2,1,2,1},{3,4,3,4},{4,3,4,3},{1,2,1,2}};
map<pair<int,int>,int> xf;
struct node{
	int x,y,z,col;
}a[N];
void add(int u,int v,int w) {
	tot++;
	nxt[tot]=head[u],head[u]=tot,to[tot]=v,cap[tot]=w;
}
inline int read() {
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=(x<<1)+(x<<3)+c-'0';
		c=getchar();
	}
	return x*f;
}
bool BFS() {
	for(int i=0;i<=rt;i++) lab[i]=0;
	lab[t]=1;
	int qhead=0,qtail=0;
	que[qtail++]=t;
	while(qhead!=qtail) {
		int u=que[qhead++];
		for(int k=head[u];k;k=nxt[k]) {
			int v=to[k];
			if(cap[k^1]==0||lab[v]>0) {
				continue;
			}
			lab[v]=lab[u]+1;
			que[qtail++]=v;
		}
	}
	return lab[s]!=0;
}
int flow(int u,int limit) {
	if(u==t) {
		return limit;
	}
	int used=0;
	for(int k=cur[u];k;k=nxt[k]) {
		int v=to[k];
		cur[u]=k;
		if(lab[u]!=lab[v]+1||cap[k]==0) {
			continue;
		}
		int tmp=min(limit-used,cap[k]);
		int ret=flow(v,tmp);
		used+=ret;
		cap[k]-=ret;
		cap[k^1]+=ret;
		if(ret==0||cap[k]==0) {
			cur[u]=nxt[k];
		}
		if(limit==used) break;
	}
	return used;
}
int calc(int x,int y) {
	return col[(x-1)%4][(y-1)%4];
}
bool outside(int x,int y) {
	if(x<1||x>r||y<1||y>c) return 1;
	return 0;
}
int dinic() {
	int tot=0;
	while(BFS()) {
		for(int i=0;i<=rt;i++) cur[i]=head[i];
		tot+=flow(s,INF);
	}
	return tot;
}
int main() {
	tot=1;
	for(int i=0;i<=rt;i++) {
	    head[i]=0;
	}
	s=rt=0;
	r=read(),c=read(),n=read();
	for(int i=1;i<=n;i++) {
		a[i].x=read(),a[i].y=read(),a[i].z=read(),a[i].col=calc(a[i].x,a[i].y);
		xf[make_pair(a[i].x,a[i].y)]=i;
		for(int j=0;j<2;j++) {
			id[j][i]=++rt;
		}
		res+=a[i].z;
	}
	t=++rt;
	for(int i=1;i<=n;i++) {
		int col=calc(a[i].x,a[i].y);
		add(id[0][i],id[1][i],a[i].z);
		add(id[1][i],id[0][i],0);
		if(col==1) {
			add(s,id[0][i],INF);
			add(id[0][i],s,0);
		}
		if(col==4) {
			add(id[1][i],t,INF);
			add(t,id[1][i],0);
		}
		for(int k=0;k<4;k++) {
			int ti=a[i].x+dx[k],tj=a[i].y+dy[k];
			if(outside(ti,tj)) continue;
			if(xf.find(make_pair(ti,tj))==xf.end()) {
				continue;
			}
			int j=xf[make_pair(ti,tj)],col2=calc(ti,tj);
			if(col+1==col2) {
				add(id[1][i],id[0][j],INF);
				add(id[0][j],id[1][i],0);
			}
		}
	}
	printf("%d",dinic());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值