dirworth定理
所以题目的第一问就是求最小可重链。
第二问和第三问就不写了,因为小粉兔写的蛮清楚的,这里只阐述做法
第二问
从二分图左侧每个未被匹配的点出发dfs
左侧的点只走未匹配点,右侧的点只走匹配点
这样下来,左侧未dfs的点和右侧dfs的点构成最小点覆盖
最大独立集就是最小点覆盖的补集,即答案
第三问
枚举点删掉,同时删去和这个点有关的所有点和边,代表选择这条链
继续跑最小可重链覆盖,如果答案为未删去时答案-1
说明可行,因为加上刚才那条链就是答案
#include <bits/stdc++.h>
using namespace std;
#define id(x,y) (x-1)*m+y
const int maxn=2e5+10;
const int inf=1e9;
int n,m,s,t,sumn,dis[maxn],ju[109][109];
struct edge{
int to,nxt,flow;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int flow){
d[++cnt]=(edge){v,head[u],flow},head[u]=cnt;
d[++cnt]=(edge){u,head[v],0},head[v]=cnt;
}
bool bfs()
{
for(int i=0;i<=t;i++) dis[i]=0;
dis[s]=1;
queue<int>q; q.push( s );
while( !q.empty() )
{
int u=q.front(); q.pop();
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( d[i].flow&&dis[v]==0 )
{
dis[v]=dis[u]+1;
if( v==t ) return true;
q.push( v );
}
}
}
return false;
}
int dinic(int u,int flow)
{
if( u==t ) return flow;
int res=flow;
for(int i=head[u];i&&res;i=d[i].nxt )
{
int v=d[i].to;
if( dis[v]==dis[u]+1&&d[i].flow)
{
int temp=dinic(v,min(res,d[i].flow) );
if( temp==0 ) dis[v]=0;
res-=temp;
d[i].flow-=temp;
d[i^1].flow+=temp;
}
}
return flow-res;
}
void floyd()
{
s=0,t=n+n+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if( i==j ) ju[i][j]=0;
else ju[i][j]=inf;
for(int i=1;i<=m;i++)
{
int l,r; cin >> l >> r;
ju[l][r]=min( ju[l][r],1 );
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ju[i][j]=min( ju[i][j],ju[i][k]+ju[k][j]);
}
int b[maxn],vis[maxn];
void dfs(int x)
{
if( vis[x] ) return;
vis[x]=1;
if( x<=n )//左侧点
{
for(int i=head[x];i;i=d[i].nxt )
if( d[i].flow ) dfs( d[i].to );
}
else
{
for(int i=head[x];i;i=d[i].nxt )
if( !d[i^1].flow ) dfs( d[i].to );
}
}
int main()
{
cin >> n >> m;
floyd();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if( ju[i][j]!=inf&&i!=j ) add(i,j+n,1);
for(int i=1;i<=n;i++)
add(s,i,1),b[i]=cnt-1,add(i+n,t,1);
int ans=n;
while( bfs() ) ans-=dinic(s,inf);
cout << ans << '\n';
//下面处理第二问
for(int i=1;i<=n;i++)
if( d[b[i]].flow ) dfs(i);//从左侧非匹配点出发
for(int i=1;i<=n;i++)
{
int k=0;
if( vis[i]&&!vis[i+n] ) k=1;//左侧访问右侧未访问
cout << k;
}
cout << '\n';
//下面处理第三问
for(int k=1;k<=n;k++)
{
memset(head,0,sizeof(head));
cnt=1; int tot=0;
for(int i=1;i<=n;i++)//删掉点k
if( i!=k&&ju[i][k]==inf&&ju[k][i]==inf )
add(s,i,1),add(i+n,t,1),tot++;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if( i!=j&&ju[i][j]<inf&&i!=k)
add(i,j+n,1);
while( bfs() ) tot-=dinic(s,inf);
cout << ((ans-1)==tot) ;
}
}