【CQOI2017】老 C 的方块

最小割。

题目大意:

给定 $C$ 行 $R$ 列的矩形,每个格子可能有方块或没方块。其中相邻两个格子之间有一条边。这条边有可能是特殊的,其分布规律如图所示:

现在有 $n$ 个格子被涂上了颜色,擦掉一个格子的颜色需要花一定的费用(不一定相同)。

不允许出现这样形状(或翻转后出现这样的情况)的涂色区域出现:

求最少要花多少钱来擦颜色才能满足条件。

题解:

最小割。

考虑如下建图方式:

每个格子先拆成入点和出点,之间连容量为擦去它的价值的边。

红色箭头表示两个格子间有连边,方向为箭头所指。

蓝色圆圈的点和源点相连,黑色圆圈的点和汇点相连。

观察可得,任意一个蓝点和黑点相连的路径,恰是不合法的情况。

我们要花最小的代价删掉一些边,使得蓝点和黑点不连通。

最小割问题,跑最大流即可。

由于这张图层数很少,所以 $10^5$ 的数据可以跑过。

Code:

#include<iostream>
#include<algorithm>
#include<map>
#include<cstring>
#include<queue>
#include<cstdio>
using namespace std;
typedef long long LL;
const int N=2e5+5;
const LL inf=0x3f3f3f3f3f3f3f3f;
queue<int>q;
struct edge{
    int to,nxt;LL cap;
}e[N*10];
int head[N],cnt=1,C,R,n,X[N],Y[N],W[N],T,dep[N],iter[N];
bool vis[N];
inline void addedge(int u,int v,LL cap){
    e[++cnt]=(edge){v,head[u],cap},head[u]=cnt;
    e[++cnt]=(edge){u,head[v],0},head[v]=cnt;
}
map<int,int>st[N];
bool bfs(){
    memset(dep,0,sizeof(int)*(T+1));
    dep[0]=1;
    q.push(0);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt)
        if(e[i].cap&&!dep[e[i].to]){
            dep[e[i].to]=dep[u]+1;
            q.push(e[i].to);
        }
    }
    return dep[T];
}
LL dfs(int u,LL f){
    if(u==T||!f)return f;
    for(int&i=iter[u];i;i=e[i].nxt)
    if(e[i].cap&&dep[e[i].to]>dep[u]){
        LL d=dfs(e[i].to,min(f,e[i].cap));
        if(d){
            e[i].cap-=d,e[i^1].cap+=d;
            return d;
        }
    }
    return 0;
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>C>>R>>n;
    T=2*n+1;
    for(int i=1;i<=n;++i){
        cin>>X[i]>>Y[i]>>W[i];
        addedge(i,i+n,W[i]);
        st[X[i]][Y[i]]=i;
    }
    for(int i=1;i<=n;++i)
    if((X[i]%4==1)&&(Y[i]%2==1)){
        int x=X[i],y=Y[i];
        int id=st[x-1][y];
        if(id){
            if(!vis[id])vis[id]=1,addedge(0,id,inf);
            addedge(id+n,i,inf);
        }
        id=st[x][y-1];
        if(id){
            if(!vis[id])vis[id]=1,addedge(0,id,inf);
            addedge(id+n,i,inf);
        }
        id=st[x][y+1];
        if(id){
            if(!vis[id])vis[id]=1,addedge(0,id,inf);
            addedge(id+n,i,inf);
        }
        id=st[x+1][y];
        if(id){
            addedge(i+n,id,inf);
        }
        ++x;
        int iid=id;
        if(!iid)continue;
        id=st[x][y-1];
        if(id){
            if(!vis[id])vis[id]=1,addedge(id+n,T,inf);
            addedge(iid+n,id,inf);
        }
        id=st[x][y+1];
        if(id){
            if(!vis[id])vis[id]=1,addedge(id+n,T,inf);
            addedge(iid+n,id,inf);
        }
        id=st[x+1][y];
        if(id){
            if(!vis[id])vis[id]=1,addedge(id+n,T,inf);
            addedge(iid+n,id,inf);
        }
    }else
    if((X[i]%4==0)&&(Y[i]%2==0)){
        int x=X[i],y=Y[i];
        int id=st[x+1][y];
        if(id){
            if(!vis[id])vis[id]=1,addedge(0,id,inf);
            addedge(id+n,i,inf);
        }
        id=st[x][y-1];
        if(id){
            if(!vis[id])vis[id]=1,addedge(0,id,inf);
            addedge(id+n,i,inf);
        }
        id=st[x][y+1];
        if(id){
            if(!vis[id])vis[id]=1,addedge(0,id,inf);
            addedge(id+n,i,inf);
        }
        id=st[x-1][y];
        if(id){
            addedge(i+n,id,inf);
        }
        --x;
        int iid=id;
        if(!iid)continue;
        id=st[x][y-1];
        if(id){
            if(!vis[id])vis[id]=1,addedge(id+n,T,inf);
            addedge(iid+n,id,inf);
        }
        id=st[x][y+1];
        if(id){
            if(!vis[id])vis[id]=1,addedge(id+n,T,inf);
            addedge(iid+n,id,inf);
        }
        id=st[x-1][y];
        if(id){
            if(!vis[id])vis[id]=1,addedge(id+n,T,inf);
            addedge(iid+n,id,inf);
        }
    }
    LL flow=0;
    while(bfs()){
        memcpy(iter,head,sizeof(int)*(T+1));
        while(int f=dfs(0,inf))flow+=f;
    }
    cout<<flow<<'\n';
    return 0;
}