思路:
用网络流跑二分图的最大匹配,先将H公司第个人的编号变为,然后表示公司和公司的两个人有关系,待匹配。残余网络中非匹配边为从左部到右部的有向边,的边权为,匹配边为从右部到左部的有向边,边权为。
非匹配边:,表示边的容量
匹配边:
因为要求不可行的边的数量,即不是可行边和必须边的边;先跑一遍最大匹配
最大匹配时,必须边一定会被包括,所以如果,那么它就不是必须边,如果边上的点又不在一个强连通分量内,那么也不是可行边,而是不可行边。
MyCode:
#include<bits/stdc++.h> using namespace std; const int maxn=2e4+7,maxm=2e5+4e4+7; int head[maxn],Next[maxm],to[maxm],edge[maxm],tot=1,d[maxn],s,t,now[maxn]; queue<int>q; void add(int x,int y,int z) { to[++tot]=y; Next[tot]=head[x]; head[x]=tot; edge[tot]=z; to[++tot]=x; Next[tot]=head[y]; head[y]=tot; edge[tot]=0; } bool bfs() { memset(d,0,sizeof d); while(q.size()) q.pop(); q.push(s); d[s]=1; now[s]=head[s]; while(q.size()) { int x=q.front(); q.pop(); for(int i=head[x],y;i;i=Next[i]) { if(edge[i] && !d[y=to[i]]) { q.push(y); now[y]=head[y]; d[y]=d[x]+1; if(y==t) return 1; } } } return 0; } int dinic(int x,int flow) { if(x==t) return flow; int rest=flow,k,i,y; for(i=now[x];i && rest; i=Next[i]) { if(edge[i] && d[y=to[i]] == d[x] + 1) { k=dinic(y,min(rest,edge[i])); if(!k) d[y]=0; edge[i]-=k; edge[i^1]+=k; rest-=k; } } now[x]=i; return flow - rest; } int dfn[maxn],low[maxn],ins[maxn],c[maxn],stk[maxn],a[maxm],b[maxm],num,cnt,top; vector<int>ans; void tarjan(int x,int in_edge) { dfn[x]=low[x]=++num; stk[++top]=x; ins[x]=1; for(int i=head[x],y;i;i=Next[i]) { if(!edge[i]) continue; if(!dfn[y=to[i]]) { tarjan(y,i); low[x]=min(low[x],low[y]); } else if(ins[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]) { ++cnt; int y; do{ y=stk[top--]; ins[y]=0; c[y]=cnt; }while(x!=y); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n,m,T; cin>>n>>m>>T; for(int i=1,x,y;i<=T;++i) { cin>>x>>y; y+=n; a[i]=x; b[i]=y; add(x,y,1); } s=0,t=n+m+1; for(int i=1;i<=n;++i) add(s,i,1); for(int i=n+1;i<=m+n;++i) add(i,t,1); while(bfs()) while(dinic(s,0x3f3f3f3f)); for(int i=0;i<=n+m+1;++i) if(!dfn[i]) tarjan(i,0); for(int i=1;i<=T;++i) if(edge[i<<1] && c[a[i]]!=c[b[i]]) ans.emplace_back(i); cout<<ans.size()<<'\n'; for(int i:ans) cout<<i<<' '; cout<<'\n'; return 0; }