CH#17C 舞动的夜晚(Dinic+tarjan)

Posted aaddvvaanntteezz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CH#17C 舞动的夜晚(Dinic+tarjan)相关的知识,希望对你有一定的参考价值。

题目链接

题意

给定一张左部$N$个点,右部$M$个点,$E$条边的二分图,求二分图最大匹配的非可行边的条数

题解

对二分图加入源点和汇点建图跑Dinic,得到一组最大匹配后对残量网络求强联通分量。则边$(x,y)$是非可行边的判定方法为:剩余容量为1且$x$,$y$不属于同一个强联通分量,时间复杂度$O(E*sqrt{N+M})$

代码

查看代码
#include <bits/stdc++.h>
using namespace std;
#define _for(i,a,b) for(int i = (a);i <= (b);++i)
typedef long long ll;
const int maxn = 1e5+5;
const int mod = 1e9+7;
struct EDGE
{
    int st,to,res,next;
}edge[maxn*4];
int tot,head[maxn],d[maxn],s,t;
void addedge(int st,int to,int c)
{
    edge[++tot].st=st;
    edge[tot].res=c;
    edge[tot].to=to;
    edge[tot].next=head[st];
    head[st]=tot;
}
bool bfs()
{
    queue<int>q;
    q.push(s);
    memset(d,0,sizeof(d));
    d[s]=1;
    while(!q.empty()){
        int now = q.front();
        q.pop();
        for(int i = head[now];i;i = edge[i].next){
            int to = edge[i].to;
            if(edge[i].res&&!d[to]){
                d[to]=d[now]+1;
                q.push(to);
                if(to==t)return 1;
            }
        }
    }
    return 0;
}
int dinic(int now,int flow)
{
    if(now==t)return flow;
    int res = flow;
    for(int i = head[now];i&&res;i = edge[i].next){
        int to = edge[i].to;
        if(d[to]==d[now]+1&&edge[i].res){
            int k = dinic(to,min(edge[i].res,res));
            if(!k)d[to]=0;
            res-=k;
            edge[i].res-=k;
            edge[i^1].res+=k;
        }
    }
    return flow-res;
}
vector<int>v[maxn];
int dfn[maxn],low[maxn],cnt,num,top,stk[maxn],instk[maxn],belong[maxn];
void tarjan(int now)
{
    low[now]=dfn[now]=++num;
    stk[++top]=now;
    instk[now]=1;
    for(int i = 0;i < v[now].size();++i){
        int to = v[now][i];
        if(!dfn[to]){
            tarjan(to);
            low[now]=min(low[now],low[to]);
        }
        else if(instk[to])low[now]=min(low[now],dfn[to]);
    }
    if(dfn[now]==low[now]){
        int x = stk[top];
        top--;
        cnt++;
        while(x!=now){
            instk[x]=0;
            belong[x]=cnt;
            x=stk[top];
            top--;
        }
        instk[now]=0;
        belong[now]=cnt;
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("simple.in","r",stdin);
    freopen("simple.out","w",stdout);
#endif
    int n,m,e;
    scanf("%d%d%d",&n,&m,&e);
    tot=1;
    s=0,t=n+m+1;
    for(int i = 1;i <= e;++i)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        b+=n;
        addedge(a,b,1);
        addedge(b,a,0);
    }
    for(int i = 1;i <= n;++i)
    {
        addedge(s,i,1);
        addedge(i,s,0);
    }
    for(int i = n+1;i <= n+m;++i)
    {
        addedge(i,t,1);
        addedge(t,i,0);
    }
    int maxflow=0;
    while(bfs())
    {
        int flow ;
        while(flow=dinic(s,1000000))maxflow+=flow;
    }
    int ti = 0;
    for(int i = 2;i <= tot;++i)
    {
        if(edge[i].res){
            ti++;
            v[edge[i].st].push_back(edge[i].to);
        }
    }
    for(int i = s;i <= t;++i)
    {
        if(!dfn[i])tarjan(i);
    }
    vector<int>ans;
    for(int i = 1;i <= e;++i)
    {
        if(edge[i*2].res&&belong[edge[i*2].st]!=belong[edge[i*2].to])ans.push_back(i);
    }
    int count = ans.size();
    printf("%d
",count);
    for(int i = 0;i < count;++i){
        if(i==count-1)printf("%d",ans[i]);
        else printf("%d ",ans[i]);
    }
    printf("
");
    return 0;
}

以上是关于CH#17C 舞动的夜晚(Dinic+tarjan)的主要内容,如果未能解决你的问题,请参考以下文章

[Contest Hunter#17-C] 舞动的夜晚

舞动的夜晚(二分图的必须边和可行边)

bzoj 1565 [NOI2009]植物大战僵尸tarjan+最大权闭合子图

dinic算法模板

利用c语言数组验证?

LCA的两种写法