BZOJ4823 CQOI2017老C的方块(最小割)

  如果将其转化为一个更一般的问题即二分图带权最小单边点覆盖(最小控制集)感觉是非常npc的。考虑原题给的一大堆东西究竟有什么奇怪的性质。

  容易发现如果与特殊边相邻的两格子都放了方块,并且这两个格子都各有另一个相邻格子放了方块,其组成的连通块就是需要破坏的。自然四个格子都可以选择破坏。可以发现如果在中间的两个格子里选的话,应该选择破坏权值较小的,因为其对其他格子没有影响。同时注意到另两个格子在黑白染色的图中一定是不同色的。

  那么做法就很显然了,建四层点,外部两层是不与特殊边相邻的黑白点,内部两层是与特殊边相邻的黑白点,外部点分别与源汇连边权为其权值的边,内部点之间连边权为较小权值的边,外部点和内部点之间连inf边,跑最小割即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
#define ll long long
#define N 100010
#define S 0
#define T 100001
#define inf 1000000010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
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^48),c=getchar();
    return x*f;
}
int c,r,n,p[N],t=-1;
int d[N],cur[N],q[N],ans;
struct data{int to,nxt,cap,flow;
}edge[N<<5];
struct data2{int x,y;
}a[N];
map<int,int> f[N],id[N];
int wx[4]={1,0,0,-1},wy[4]={0,1,-1,0};
void addedge(int x,int y,int z)
{
    t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,edge[t].flow=0,p[x]=t;
    t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,edge[t].flow=0,p[y]=t;
}
bool isblack(int x,int y){return x+y&1;}
bool isnear(int x,int y){return x&1?y%4==1||y%4==2:y%4==0||y%4==3;}
bool bfs()
{
    memset(d,255,sizeof(d));d[S]=0;
    int head=0,tail=1;q[1]=S;
    do
    {
        int x=q[++head];
        for (int i=p[x];~i;i=edge[i].nxt)
        if (d[edge[i].to]==-1&&edge[i].flow<edge[i].cap)
        {
            d[edge[i].to]=d[x]+1;
            q[++tail]=edge[i].to;
        }
    }while (head<tail);
    return ~d[T];
}
int work(int k,int f)
{
    if (k==T) return f;
    int used=0;
    for (int i=cur[k];~i;i=edge[i].nxt)
    if (d[k]+1==d[edge[i].to])
    {
        int w=work(edge[i].to,min(f-used,edge[i].cap-edge[i].flow));
        edge[i].flow+=w,edge[i^1].flow-=w;
        if (edge[i].flow<edge[i].cap) cur[k]=i;
        used+=w;if (used==f) return f;
    }
    if (used==0) d[k]=-1;
    return used;
}
void dinic()
{
    while (bfs())
    {
        memcpy(cur,p,sizeof(p));
        ans+=work(S,inf);
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4823.in","r",stdin);
    freopen("bzoj4823.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    c=read(),r=read(),n=read();
    memset(p,255,sizeof(p));
    for (int i=1;i<=n;i++)
    {
        int y=read(),x=read(),z=read();
        f[a[i].x=x][a[i].y=y]=z;id[x][y]=i;
    }
    for (int i=1;i<=n;i++)
    if (!isnear(a[i].x,a[i].y)&&isblack(a[i].x,a[i].y))
    {
        addedge(S,i,f[a[i].x][a[i].y]);
        for (int j=0;j<4;j++)
        if (isnear(a[i].x+wx[j],a[i].y+wy[j])&&id[a[i].x+wx[j]][a[i].y+wy[j]])
            for (int k=0;k<4;k++)
            if (isnear(a[i].x+wx[j]+wx[k],a[i].y+wy[j]+wy[k])&&id[a[i].x+wx[j]+wx[k]][a[i].y+wy[j]+wy[k]])
                for (int x=0;x<4;x++)
                {
                    int u=a[i].x+wx[j]+wx[k]+wx[x],v=a[i].y+wy[j]+wy[k]+wy[x];
                    if (!isnear(u,v)&&id[u][v]) addedge(i,id[a[i].x+wx[j]][a[i].y+wy[j]],inf),addedge(id[a[i].x+wx[j]+wx[k]][a[i].y+wy[j]+wy[k]],id[u][v],inf);
                }
    }
    for (int i=1;i<=n;i++)
    if (!isnear(a[i].x,a[i].y)&&!isblack(a[i].x,a[i].y)) addedge(i,T,f[a[i].x][a[i].y]);
    for (int i=1;i<=n;i++)
    if (isnear(a[i].x,a[i].y)&&!isblack(a[i].x,a[i].y))
        for (int j=0;j<4;j++)
        if (isnear(a[i].x+wx[j],a[i].y+wy[j])&&id[a[i].x+wx[j]][a[i].y+wy[j]])
        addedge(i,id[a[i].x+wx[j]][a[i].y+wy[j]],min(f[a[i].x][a[i].y],f[a[i].x+wx[j]][a[i].y+wy[j]]));
    dinic();
    cout<<ans;
    return 0;
}

 

posted @ 2018-11-22 19:09  Gloid  阅读(159)  评论(0编辑  收藏  举报