LGP1407题解

这题还有点儿意思,记录一下。

题意:给定一个二分图完备匹配,询问去掉一条匹配边后是否仍存在完备匹配。询问互相不影响。

寻找二分图最大匹配时使用的匈牙利算法就是不断寻找增广路,然后将边权取反。正确性是取反后一定还是一个匹配,且最大匹配只增不减。

那么我们可以下一个结论:如果完备匹配的边在一个“增广环”上,那么将“增广环”上的边权取反仍然是二分图完备匹配。

所以我们只需要判断匹配边是否在环上即可。显然如果不在环上,那么这条边必选,不在环上时无法找到别的完备匹配。

但是这样可能会找到一个环并不是匹配边和非匹配边交替的。

于是我们想个办法让环上的边是匹配和非匹配交替,对每个边开一个点,只连接左右端点连接的匹配状态和自身不同的边。

容易知道每条非匹配边只会连两条边,所以是对的。

继续简化模型,将一对夫妻放在一起。

观察一下这些关系,都是从女方连向男方的。

所以完全可以直接建立有向图直接找环。

因此只需要 Tarjan 缩点即可,复杂度 \(O(n+m)\)

upd:傻逼字符串题能不能少来点儿???傻逼字符串题能不能少来点儿???傻逼字符串题能不能少来点儿???

#include<cstdio>
#include<cctype>
#include<map>
const int M=24005;
int n,m,ege,dfc,BCC,sz[M],h[M],bl[M],low[M],dfn[M];int tp,stk[M];bool itk[M];std::map<int,int>hash;
struct Edge{
	int v,nx;
}e[M*4];
inline int min(const int&a,const int&b){
	return a>b?b:a;
}
inline void Add(const int&u,const int&v){
	e[++ege]=(Edge){v,h[u]};h[u]=ege;
}
inline void Tarjan(const int&u){
	itk[stk[++tp]=u]=true;low[u]=dfn[u]=++dfc;
	for(int v,E=h[u];E;E=e[E].nx){
		if(!dfn[v=e[E].v])Tarjan(v),low[u]=min(low[u],low[v]);
		else if(itk[v])low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){
		int v;++BCC;do itk[v=stk[tp--]]=false,++sz[bl[v]=BCC];while(u^v);
	}
}
inline int Hash(char*s){
	const int mod=1e9+7;int H(0);while(isalpha(*s))H=(13331ll*H+*s)%mod,*s++=0;return H;
}
inline int read_s(){
	static char s[105];scanf("%s",s);return Hash(s);
}
signed main(){
	scanf("%d",&n);for(int i=1;i<=n;++i)hash[read_s()]=hash[read_s()]=i;
	scanf("%d",&m);for(int i=1;i<=m;++i)Add(hash[read_s()],hash[read_s()]);
	for(int i=1;i<=n;++i)if(!dfn[i])Tarjan(i);for(int i=1;i<=n;++i)printf("%s",sz[bl[i]]==1?"Safe\n":"Unsafe\n");
}
posted @ 2022-06-18 10:26  Prean  阅读(20)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};