强连通分量 残量网络 容量 流量 必须边和可行边判定 Acwing380 舞动的夜晚

二分图必须边和可行边的判定定理(完备匹配)

一 (x,y)是必须边 当且仅当以下两个条件都满足

1.(x,y)当前是匹配边

2. 删除边(x,y)后,不能找到另一条从x到y的增广路。

二 (x,y)是可行边 ,当且仅当满足以下两个条件之一

1.(x,y)当前是匹配边

2.(x,y)是非匹配边,设当前x与v匹配,y与u匹配,连接边(x,y)后,节点u,v失去匹配。必须能找到一条从u到v的增广路。

G是二分图 那么把G中非匹配边看作从左部到右部的有向边,把匹配边看作从右部到左部的有向边,构成一张新的有向图,记作G1,那么G中从x到y有增广路,等价于G1存在从x到y路径

二分图必须边和可行边的判定定理可改写为(完备匹配)

一 (x,y)是必须边 当且仅当以下两个条件都满足

1.(x,y)当前是匹配边

2. x,y两点在G1属于不同强连接分量

二 (x,y)是可行边 ,当且仅当满足以下两个条件之一

1.(x,y)当前是匹配边

2.x,y两点在G1属于相同强连接分量

二分图必须边和可行边的判定定理可改写为(非完备匹配)

一 (x,y)是必须边 当且仅当以下两个条件都满足

1.(x,y)流量为1

2. x,y两点在残量网络属于不同强连接分量

二 (x,y)是可行边 ,当且仅当满足以下两个条件之一

1.(x,y)当前是匹配边

2.x,y两点在残量网络属于相同强连接分量

其中残量网络是在Dinic算法之后对剩余的容量为1的边建立的新图

其中剩余容量为0的边代表流量为1

题目链接

题目大意

求非完备二分图不可行边个数

题目思路

给二分图加一个源点汇点后 使用最大流Dinic模板求出每条边的流量和残量网络 然后再用tarjan模板求出强连通分量

逐边判断是否为非可行边

代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
const int M=5e6+10;
int n, m, t;
bool vis[N];
vector<int> mp[N];
vector<int> ans;
struct Dinic{//Dinic模板 
    int cnt = 1, s, t, INF = 1 << 29;
    int head[N], d[N], now[N];
    struct Edge{int nxt, to, val;} ed[M];
    queue<int>q;

    void add(int u, int v, int w){
        ed[++ cnt] = (Edge){head[u], v, w}; head[u] = cnt;
        ed[++ cnt] = (Edge){head[v], u, 0}; head[v] = cnt;
    }

    bool bfs(){
        while(!q.empty()) q.pop();
        memset(d, 0, sizeof(d));
        q.push(s); d[s] = 1; now[s] = head[s];
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i; i = ed[i].nxt){
                int v = ed[i].to, w = ed[i].val;
                if(!d[v] && w){
                    now[v] = head[v];
                    d[v] = d[u] + 1;
                    if(v == t) return true;
                    q.push(v);
                }
            }
        }
        return false;
    }

    int dinic(int u, int flow){
        if(u == t) return flow;
        int rest = flow, i;
        for(i = now[u]; i && rest; i = ed[i].nxt){
            int v = ed[i].to, w = ed[i].val;
            if(w && d[v] == d[u] + 1){
                int k = dinic(v, min(rest, w));
                if(!k) d[v] = 0;
                ed[i].val -= k;
                ed[i ^ 1].val += k;
                rest -= k;
            }
        }
        now[u] = i;
        return flow - rest;
    }

    void work(int S, int T){
        s = S, t = T;
        int flow = 0, maxflow = 0;
        while(bfs())
            while(flow = dinic(s, INF)) maxflow += flow;
        for(int u = s; u <= t; u ++)//建残量网络新图。
            for(int i = head[u]; i; i = ed[i].nxt){
                int v = ed[i].to, w = ed[i].val;
                if(w) mp[u].push_back(v);
                else vis[i] = true;
                //在残量网络中,容量为 0 说明流量为 1,反之同理。
            }
    }
} G;
int top, tot, num;
int dfn[N], low[N], st[N], c[N];
void tarjan(int u){//tarjan模板 
    dfn[u] = low[u] = ++ num;
    st[++ top] = u;
    for(int i = 0; i < mp[u].size(); i ++){
        int v = mp[u][i];
        if(!dfn[v])
            tarjan(v), low[u] = min(low[u], low[v]);
        else if(!c[v])
            low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u]){
        int v; tot ++;
        do{
            v = st[top --];
            c[v] = tot;
        }while(v != u);
    }
}
int S,T;
int main()
{
	cin>>n>>m>>t;
	S=0;
	T=n+m+1;
	int x,y;
	for(int i=1;i<=t;i++)
	{
		scanf("%d %d",&x,&y);
		G.add(x,y+n,1);
	}
	for(int i=1;i<=n;i++)G.add(S,i,1);
	for(int i=1;i<=m;i++)G.add(i+n,T,1);
	G.work(S,T);
	for(int i=S;i<=T;i++)
	{
		if(!dfn[i])tarjan(i);
	}
	for(int i=2;i<=2*t+1;i+=2)
	{
		int u=G.ed[i].to,v=G.ed[i^1].to;
		if(vis[i]==0&&c[u]!=c[v])ans.push_back(i/2);
	}
	cout<<ans.size()<<endl;
	for(int i=0;i<ans.size();i++)cout<<ans[i]<<" ";
	cout<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值