题解:P4547 [THUWC 2017] 随机二分图

link

发现期望的完美匹配数量也就相当于每个完美匹配出现的概率之和,因而我们只需要求每个完美匹配的概率即可。

考虑只有 \(0\) 类边的情况,我们可以设 \(f_{i,T}\) 表示左部的前 \(i\) 个点匹配完后,右部点中被匹配的集合是 \(T\) 的概率,然后进行转移即可。

接下来考虑有 \(1\) 类边的情况,由于需要同时加入两条边,我们将状态改为 \(f_{S,T}\) 表示左部匹配了 \(S\) 中的点,右部匹配了 \(T\) 中的点的概率,注意有 \(|S|=|T|\)。我们依然可以将每条边按 \(\frac12\) 的概率加入,发现此时两条边都选的概率是 \(\frac14\),而事实上是 \(\frac12\), 因而我们只需加入一个包含两条边且概率为 \(\frac14\) 的组即可将概率补齐。

类似的,对于二类边,两条边都选的概率是 \(\frac14\),而事实上是 \(0\), 因而我们只需加入一个包含两条边且概率为 \(-\frac14\) 的组即可将概率补齐。

为了避免重复,我们每次都要类似只有 \(0\) 类边时选编号最小的左部未匹配点来匹配。

由于 \(|S|=|T|\),状态数为 \(\sum_{i=0}^n\binom{n}{i}^2=\binom {2n}n\approx1.5⋅10^8\),时间复杂度即 \(O(\binom {2n}nn^2)\) 使用记忆化搜索可以通过。

参考资料:LOJ2290 「THUWC 2017」随机二分图

Code:

#include<iostream>
#include<map>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,l,r) for(int i=(l);i>=(r);i--)
using namespace std;
const int maxm=1000,mod=1e9+7;
typedef long long ll;
inline ll qpow(ll a,int b){
	ll res=1;
	while(b){
		if(b&1)res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}
int a1[maxm],ab[maxm],ms,in,im,iv2,iv4,tot;
map<int,int> mp;
inline ll dfs(int s){//这里将 S 和 T 一起存储到 s 里
	if(s==ms)return 1;
	if(mp.count(s))return mp[s];
	ll res=0,p=0;
	rep(v1,0,in-1)if(!(s&(1<<v1))){
		p=1<<v1;
		break;
	}
	rep(v1,1,tot)if(!(s&a1[v1])&&(a1[v1]&p)){
		res=(res+ab[v1]*dfs(s|a1[v1]))%mod;
	}
	return mp[s]=res;
}
int main(){
	cin>>in>>im;
	iv2=qpow(2,mod-2);
	iv4=qpow(4,mod-2);
	ms=(1<<in*2)-1;
	rep(v1,1,im){
		int iop,ix,iy;
		scanf("%d %d %d",&iop,&ix,&iy);
		int s=(1<<ix-1)|(1<<iy+in-1);
		a1[++tot]=s;
		ab[tot]=iv2;
		if(iop){
			scanf("%d %d",&ix,&iy);
			a1[++tot]=(1<<ix-1)|(1<<iy+in-1);
			ab[tot]=iv2;
			if(s&((1<<ix-1)|(1<<iy+in-1)))continue;
			a1[++tot]=s|(1<<ix-1)|(1<<iy+in-1);
			ab[tot]=(iop==1?iv4:mod-iv4);
		}
	}
	cout<<dfs(0)*qpow(2,in)%mod<<endl;
	return 0;
}
posted @ 2025-03-06 20:10  FugiPig  阅读(28)  评论(0)    收藏  举报