P4298 [CTSC2008]祭祀(dirworth+各种二分图定理)

D A G ( ) 大前提是DAG(有向无环图)

: u , v u v v u 链:一个点集中任意两点u,v存在u-v或v-u

: u , v 反链:一个点集中任意两点u,v不可达

: 使 , 1 最小路径覆盖:使用最少的链去覆盖所有点,且每个点只被覆盖1次

: 使 , 最小可重链:使用最少的链去覆盖所有点,且每个点可被覆盖多次

dirworth定理

D A G , , = DAG图中,数值上,最长反链=最小可重链

这里不证明

所以题目的第一问就是求最小可重链。

24 , 最小路径覆盖问题在网络流24题中有,这个也差不多

, 但是因为每个点可经过多次,所以不按照题目给的边来建造

f l o y d , 而是floyd求出每个点能走到的点,这些都算一条边

第二问和第三问就不写了,因为小粉兔写的蛮清楚的,这里只阐述做法

第二问

从二分图左侧每个未被匹配的点出发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) ;
	} 
}

猜你喜欢

转载自blog.csdn.net/jziwjxjd/article/details/108309928