[二分图最大匹配必配点] BZOJ 3546 [ONTAK2010]Life of the Party

%%%dyh

首先求出最大匹配,下面考虑左边点的情况。
我们将匹配中的边从右往左连,不在匹配中的边从左往右连。
这个时候一条增广路成为一条连续的路径。
从每个左边未匹配的点还是遍历,如果被一个左边的点被访问到,说明存在一条增广路,也就是不一定在最大匹配中。
所有没有被访问到的点一定在最大匹配中。

#include<cstdio>    
#include<cstdlib>    
#include<algorithm>    
#include<cstring>    
#define cl(x) memset(x,0,sizeof(x))    
#define V G[p].v    
using namespace std;    
     
inline char nc()    
{    
    static char buf[100000],*p1=buf,*p2=buf;    
    if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }    
    return *p1++;    
}    
     
inline void read(int &x){    
    char c=nc(),b=1;    
    for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;    
    for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;    
}    
     
namespace DINIC{    
    const int N=20005;    
    const int M=300005;    
    struct edge{    
        int u,v,f;    
        int next;    
    }G[M];    
    int head[N],inum=1;    
    inline void add(int u,int v,int f,int p){    
        G[p].u=u; G[p].v=v; G[p].f=f; G[p].next=head[u]; head[u]=p;    
    }    
    inline void link(int u,int v,int f){    
        add(u,v,f,++inum); add(v,u,0,++inum);    
    }    
    int S,T;    
    int dis[N];    
    int Q[N],l,r;    
    inline bool bfs(){    
        memset(dis,-1,sizeof(dis)); l=r=-1;    
        Q[++r]=S; dis[S]=1;    
        while (l<r){    
            int u=Q[++l];    
            for (int p=head[u];p;p=G[p].next)    
                if (G[p].f && dis[V]==-1){    
                    dis[V]=dis[u]+1; Q[++r]=V;    
                    if (V==T) return 1;    
                }    
        }    
        return 0;    
    }    
    int cur[N];    
    int dfs(int u,int flow){    
        if (u==T) return flow;    
        int used=0,now;    
        for (int p=cur[u];p;p=G[p].next)    
        {    
            cur[u]=p;    
            if (G[p].f && dis[V]==dis[u]+1)    
            {    
                now=dfs(V,min(flow-used,(int)G[p].f));    
                G[p].f-=now,G[p^1].f+=now;    
                used+=now; if (flow==used) break;    
            }    
        }    
        if (!used) dis[u]=-1;    
        return used;    
    }    
    int Dinic(){    
        int ret=0;    
        while (bfs())    
            memcpy(cur,head,sizeof(cur)),ret+=dfs(S,1<<30);    
        return ret;    
    }    
}    
  
const int N=100005;  
  
int n,m,K;  
int x[N],y[N];  
int left[N],right[N];  
  
inline void Match()  
{  
    using namespace DINIC;  
    S=n+m+1,T=n+m+2;  
    for (int i=1;i<=K;i++)  
        link(x[i],n+y[i],1<<30);  
    for (int i=1;i<=n;i++)  
        link(S,i,1);  
    for (int i=1;i<=m;i++)  
        link(n+i,T,1);  
    Dinic();  
    for (int i=1;i<=K;i++)  
    {  
        int p=2*i;  
        if (G[p^1].f) left[y[i]]=x[i],right[x[i]]=y[i];  
    }  
}  
  
struct edge{  
    int u,v,next;  
}G[N<<3];  
int head[N],inum;  
  
inline void add(int u,int v,int p){  
    G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;  
}  
  
int vst[N<<2];  
int boy[N],girl[N];  
  
#define V G[p].v  
inline void dfs(int u)  
{  
    if (vst[u]) return;  
    vst[u]=1;  
    for (int p=head[u];p;p=G[p].next)  
        dfs(V);  
}  
  
int main()  
{   
    read(n); read(m); read(K);  
    for (int i=1;i<=K;i++)  
        read(x[i]),read(y[i]);  
    Match();  
    for (int i=1;i<=K;i++)  
        if (left[y[i]]==x[i])  
            add(n+y[i],x[i],++inum);  
        else  
            add(x[i],n+y[i],++inum);  
    for (int i=1;i<=n;i++)  
        if (!right[i])  
            dfs(i);  
    for (int i=1;i<=n;i++)  
        if (!vst[i])  
            printf("%d\n",i);  
    cl(vst); cl(head); inum=0;  
    for (int i=1;i<=K;i++)  
        if (left[y[i]]!=x[i])  
            add(n+y[i],x[i],++inum);  
        else  
            add(x[i],n+y[i],++inum);  
    for (int i=1;i<=m;i++)  
        if (!left[i])  
            dfs(n+i);  
    for (int i=1;i<=m;i++)  
        if (!vst[n+i])  
            printf("%d\n",i);  
    return 0;  
}  


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值