luogu P3756 [CQOI2017]老C的方块(最小割&四色染色)

luogu P3756 [CQOI2017]老C的方块(最小割&四色染色)

题目大意

老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列之间也有特殊边,而所在行的编号从左到右奇偶交替,所以它们的特殊边在偶数行。如果网格的规模更大,我们可以用同样的方法找出所有的特殊边。

img

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

img

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

解题思路

对原图染上四种颜色,以一二行的前8个为例,后面的以此为周期重复

在这里插入图片描述

其中灰色的线就是原题目中所说的直线。

在最小割中同时满足就是并联,满足任一就是串联,考虑建下面的图

在这里插入图片描述

其中黄色格子和源点之间的边的容量为 黄色点的权值,黄色与红色点之间的容量为inf,红色与蓝色点之间的容量为红蓝颜色中权值较小的权值,蓝绿之间的权值为inf,绿色与汇点直接的容量为绿色的点的权值。

最后跑出最小割就是答案

AC代码

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
const int maxm=5e6+5;
const LL inf=1e12;
struct Edge{
    int to,nxt;
	LL cap,flow;
}edge[maxm];
int tol;
int head[maxn];
inline void init(){
    tol=2;
    memset(head,-1,sizeof(head));
}
inline void AddEdge(int u,int v,LL w,int rw=0){
    edge[tol].to=v;edge[tol].cap=w;edge[tol].flow=0;
    edge[tol].nxt=head[u];head[u]=tol++;
    edge[tol].to=u;edge[tol].cap=rw;edge[tol].flow=0;
    edge[tol].nxt=head[v];head[v]=tol++;
}
int Q[maxn];
int dep[maxn],cur[maxn],sta[maxn];
inline bool bfs(int s,int t,int n){
    int front=0,tail=0;
    memset(dep,-1,sizeof(dep[0])*(n+1));
    dep[s]=0;
    Q[tail++]=s;
    while(front<tail){
        int u=Q[front++];
        for(register int i=head[u];i!=-1;i=edge[i].nxt){
            int v=edge[i].to;
            if(edge[i].cap>edge[i].flow&&dep[v]==-1){
                dep[v]=dep[u]+1;
                if(v==t) return true;
                Q[tail++]=v;
            }
        }
    }
    return false;
}
inline LL dinic(int s,int t,int n){
    LL maxflow=0;
    while(bfs(s,t,n)){
        for(register int i=0;i<n;++i) cur[i]=head[i];
        int u=s,tail=0;
        while(cur[s]!=-1){
            if(u==t){
                LL tp=inf;
                for(register int i=tail-1;i>=0;--i)
                {
                    tp=min(tp,edge[sta[i]].cap-edge[sta[i]].flow);
                }
                maxflow+=tp;
                for(register int i=tail-1;i>=0;--i){
                    edge[sta[i]].flow+=tp;
                    edge[sta[i]^1].flow-=tp;
                    if(edge[sta[i]].cap-edge[sta[i]].flow==0) tail=i;
                }
                u=edge[sta[tail]^1].to;
            }
            else if(cur[u]!=-1&&edge[cur[u]].cap>edge[cur[u]].flow&&dep[u]+1==dep[edge[cur[u]].to]){
                sta[tail++]=cur[u];
                u=edge[cur[u]].to;
            }
            else{
                while(u!=s&&cur[u]==-1) u=edge[sta[--tail]^1].to;
                cur[u] = edge [cur[u]].nxt;
            }
        }
    }
    return maxflow;
}
struct node{
	int x,y,w,id;
	node(){}
	node(int x,int y,int w,int id):x(x),y(y),w(w),id(id){}
}block[100005];
typedef pair<int,int> pii;
map<pii,int> ids;
int col[100005];
inline int color_maker(int x,int y)
{
	//1 red 2 blue 3 green 4 yellow
	if(y&1)
	{
		if(x%4==1) return 1;
		if(x%4==2) return 2;
		if(x%4==3) return 3;
		if(x%4==0) return 4;
	}
	else
	{
		if(x%4==1) return 4;
		if(x%4==2) return 3;
		if(x%4==3) return 2;
		if(x%4==0) return 1;
	}
}
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int main()
{
	int c,r,n;
	init();
	scanf("%d%d%d",&c,&r,&n);
	ids.clear();
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&block[i].x,&block[i].y,&block[i].w);
		block[i].id=i;
		col[i]=color_maker(block[i].x,block[i].y);
		ids[pii(block[i].x,block[i].y)]=i;
	}
	int t=n+1;
	int s=0;
	for(int i=1;i<=n;i++)
	{
		int x=block[i].x,y=block[i].y;
		if(col[i]==1) 
		{
			for(int j=0;j<4;j++)
			{
				int xx=x+dir[j][0],yy=y+dir[j][1];
				if(!ids.count(pii(xx,yy))) continue;
				int idnew=ids[pii(xx,yy)];
				if(col[idnew]==4) AddEdge(idnew,i,inf);
				if(col[idnew]==2) AddEdge(i,idnew,min(block[i].w,block[idnew].w));
			}
		}
		if(col[i]==2) 
		{
			for(int j=0;j<4;j++)
			{
				int xx=x+dir[j][0],yy=y+dir[j][1];
				if(!ids.count(pii(xx,yy))) continue;
				int idnew=ids[pii(xx,yy)];
				if(col[idnew]==3) AddEdge(i,idnew,inf);
			}
		}
		if(col[i]==3) AddEdge(i,t,block[i].w);
		if(col[i]==4) AddEdge(s,i,block[i].w);
	}
	printf("%d\n",dinic(s,t,t+1));
}		
if(col[idnew]==3) AddEdge(i,idnew,inf);
		}
	}
	if(col[i]==3) AddEdge(i,t,block[i].w);
	if(col[i]==4) AddEdge(s,i,block[i].w);
}
printf("%d\n",dinic(s,t,t+1));

}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值