舞动的夜晚 CH Round #17
描述 L公司和H公司举办了一次联谊晚会。晚会上,L公司的N位员工和H公司的M位员工打算进行一场交际舞。在这些领导中,一些L公司的员工和H公司的员工之间是互相认识的,这样的认识关系一共有T对。舞会上,每位员工会尝试选择一名Ta认识的对方公司的员工作为舞伴,并且每位员工至多跳一支舞。完成的交际舞的数量越多,晚会的气氛就越热烈。顾及到晚会的气氛,员工们希望知道,哪些员工之间如果进行了交际舞,就会使整场晚会能够完成的交际舞的最大数量减小。 输入格式 第一行三个整数N、M、T。 接下来T行每行两个整数x、y,表示L公司的员工x和H公司的员工y互相认识。 输出格式 第一行一个整数cnt,表示进行了交际舞后会使整场晚会能够完成的交际舞的最大数量减小的员工有多少对。 第二行cnt个整数,升序输出这样的一对员工的认识关系的编号(他们的认识关系是在输入数据中读入的第几条认识关系)。如果cnt=0,输出一个空行。 样例输入 3 3 6 1 1 2 1 2 2 3 1 3 2 3 3 样例输出 3 2 4 5 数据范围与约定 对于50%的数据,1<=N,M<=100,1<=T<=1000。 对于100%的数据,1<=N,M<=10000,1<=T<=100000,1<=x<=N,1<=y<=M题目简述
给你一个二分图,问你有多少条边满足:固定了这条边必须匹配,剩下的匹配不到最大匹配
这是模拟赛中的一道题,打完以后看了看题解,找了几个样例发现都可以证明,但是却一直没有弄懂跑Tarjan前到底要为什么要那样建图,后来听Xminh讲解之后才懂,现在说一下似懂非懂的我的想法,下面是题解建图方法
对于100%的数据,先用Dinic求任意一组最大匹配,然后建一张新图: 匹配边(i,j) j到i连边 非匹配边 (i,j) i到j连边 匹配的左点i (i,S) 不匹配的左点i (S,i) 匹配的右点j (T,j) 不匹配的右点j (j,T) 然后用Tarjan求强连通分量 (i,j)是可行边的条件: (i,j)是匹配边 或者 i,j在同一个scc里 那么总边数减去可行边数就是不可行边数,即答案。 注意这个新图要包含源和汇,不能只在二分图两部之间连边,除非原最大匹配是一个完备匹配。
首先跑一遍Dinic,然后就会发现,对于新建的图,其实就是Dinic的残量网络,此时正边就是没有跑的,反边就是在Dinic中跑了的(匹配边)
很明显,对于一条匹配边,强制它匹配对结果并无影响
然后跑一遍Tarjan找环,对于一个环来说,如果环中的一条边取反(匹配边变为不匹配或不匹配边匹配上),那么剩下的边也肯定可以取反调整来使整个环依然保持是个环,整个图也是没问题的
如果不是匹配边还不在一个环中,那么这个边如果强制连上,由于每一个点只能走一次(一个人只能匹配一个人),所以肯定有环遭到破坏,图就不合法了
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<stack> #define MAXN (200010) #define INF (21000000) #define fr(i,s,t) for (i=s;i<=t;i++) using namespace std; int N,M,T,m1=1,d[MAXN],cur[MAXN],h[MAXN],dfn[MAXN],low[MAXN],C[MAXN],dfs_num,C_num,res[MAXN]; bool F[MAXN],f[MAXN],v[MAXN]; stack <int> st; struct rel{ int x,y; }R[MAXN]; struct edge{ int next,to,cap,flow; edge(){}; edge(int N,int T,int C,int F): next(N),to(T),cap(C),flow(F){} }q[MAXN*40]; void addedge(int x,int y,int cap){ q[++m1]=edge(h[x],y,cap,0); h[x]=m1; if (cap!=-1) q[++m1]=edge(h[y],x,0,0),h[y]=m1; } bool Bfs(){ queue <int> Q; Q.push(0); memset(d,0,sizeof(d)); d[0]=1; int i,x,y; while (!Q.empty()){ x=Q.front(); Q.pop(); for (i=h[x];i;i=q[i].next){ y=q[i].to; if (q[i].cap==q[i].flow) continue; if (!d[y]){ d[y]=d[x]+1; Q.push(y); } } } return d[N+M+1]; } int dfs(int x,int a){ if (x==N+M+1||!a) return a; int flow=0,f,y; for (int &i=cur[x];i;i=q[i].next){ y=q[i].to; if (d[x]+1==d[y]&&(f=dfs(y,min(a,q[i].cap-q[i].flow)))>0){ a-=f; flow+=f; q[i].flow+=f; q[i^1].flow-=f; if (!a) break; } } return flow; } void Tarjan(int x){ dfn[x]=++dfs_num; low[x]=dfs_num; v[x]=1; st.push(x); int i,y; for (i=h[x];i;i=q[i].next){ y=q[i].to; if (!dfn[y]){ Tarjan(y); low[x]=min(low[x],low[y]); } else if (v[y]) low[x]=min(low[x],dfn[y]); } if (dfn[x]==low[x]){ v[x]=0; C[x]=++C_num; while ((y=st.top())!=x){ st.pop(); C[y]=C_num; v[y]=0; } st.pop(); } } int main(){ int i,x,y,ans=0; scanf("%d %d %d",&N,&M,&T); fr(i,1,T){ scanf("%d %d",&x,&y); R[i].x=x; R[i].y=y+N; addedge(x,y+N,1); } fr(i,1,N) addedge(0,i,1); fr(i,N+1,N+M) addedge(i,N+M+1,1); while (Bfs()){ fr(i,0,N+M+1) cur[i]=h[i]; dfs(0,INF); } fr(i,1,T) if (q[2*i].cap==q[2*i].flow) F[i]=1; m1=0; memset(q,0,sizeof(q)); memset(h,0,sizeof(h)); fr(i,1,T) if (F[i]){ f[R[i].x]=f[R[i].y]=1; addedge(R[i].y,R[i].x,-1); } else addedge(R[i].x,R[i].y,-1); fr(i,1,N) if (f[i]) addedge(i,0,-1); else addedge(0,i,-1); fr(i,N+1,N+M) if (f[i]) addedge(N+M+1,i,-1); else addedge(i,N+M+1,-1); fr(i,0,N+M+1) if (!dfn[i]) Tarjan(i); fr(i,1,T) if (!F[i]&&C[R[i].x]!=C[R[i].y]) res[++res[0]]=i; printf("%d\n",res[0]); fr(i,1,res[0]) printf("%d ",res[i]); }