[CF1229E]Marek and Matching

发布时间 2023-09-22 20:38:08作者: 灰鲭鲨

This is a harder version of the problem. In this version, \(n \le 7\).

Marek is working hard on creating strong test cases to his new algorithmic problem. Do you want to know what it is? Nah, we're not telling you. However, we can tell you how he generates test cases.

Marek chooses an integer \(n\) and \(n^2\) integers \(p_{ij}\) (\(1 \le i \le n\), \(1 \le j \le n\)). He then generates a random bipartite graph with \(2n\) vertices. There are \(n\) vertices on the left side: \(\ell_1, \ell_2, \dots, \ell_n\), and \(n\) vertices on the right side: \(r_1, r_2, \dots, r_n\). For each \(i\) and \(j\), he puts an edge between vertices \(\ell_i\) and \(r_j\) with probability \(p_{ij}\) percent.

It turns out that the tests will be strong only if a perfect matching exists in the generated graph. What is the probability that this will occur?

It can be shown that this value can be represented as \(\frac{P}{Q}\) where \(P\) and \(Q\) are coprime integers and \(Q \not\equiv 0 \pmod{10^9+7}\). Let \(Q^{-1}\) be an integer for which \(Q \cdot Q^{-1} \equiv 1 \pmod{10^9+7}\). Print the value of \(P \cdot Q^{-1}\) modulo \(10^9+7\).

\(n\le 7\),是一个很暴力的范围。

但是怎么暴力?

我们在搜索的过程中,如何很好的维护出最大匹配的信息?

考虑维护每一个左端点集合是否存在最大匹配,然后每次新增一个左端点,枚举这个点能到的集合,设为 \(s\),可以预处理出概率。然后再更新那些有最大匹配的概率。复杂度明显是爆的。

略微优化一下搜索过程,在搜索时处理出这个左端点如果连到了点 \(i\) 会有哪些最大匹配改变,可以用 __int128 状压,记为 \(f_i\)。然后枚举对应的集合时可以直接把所有 \(f_j(j\in s)\) 或起来就可以了。

可以考虑记忆化,用一个状压记录每个集合是否有最大匹配(状压的状压?有点绕)。然后就过了。记录的时候可以用一个 __int128 记到 map 里面,然后就可以过了。

#include<bits/stdc++.h>
using namespace std;
typedef unsigned __int128 LL;
const int N=10,P=1e9+7,S=1<<N;
int pown(int x,int y)
{
	if(!y)
		return 1;
	int t=pown(x,y>>1);
	if(y&1)
		return 1LL*t*t%P*x%P;
	return 1LL*t*t%P;
}
int n,p[N][N],f[N][S],cnt;
map<LL,int>mp[N];
LL mx;
void write(LL x)
{
	if(!x)
		return;
	write(x/10);
	putchar(x%10+48);
}
int dfs(int x,LL s)
{
	/*printf("%d ",x),write(s);
	puts("");*/
	if(x==n)
	{
		return s==mx;
	}
	if(mp[x].find(s)!=mp[x].end())
		return mp[x][s];
	++cnt;
	LL f[N];
	for(int i=0;i<n;i++)
		f[i]=0;
	int ans=0;
	for(int i=0;i<(1<<n);i++)
	{
		if(s>>i&1)
			for(int k=0;k<n;k++)
				f[k]|=((LL)1)<<(i|(1<<k));
	}
	for(int i=1;i<(1<<n);i++)
	{
		LL to=s;
		for(int j=0;j<n;j++)
			if(i>>j&1)
				to|=f[j];
		ans+=dfs(x+1,to)*1LL*::f[x][i]%P;
		if(ans>=P)
			ans-=P;
	}
	return mp[x][s]=ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<(1<<n);i++)
		mx|=((LL)1)<<i;
	int pw=pown(100,P-2);
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			scanf("%d",p[i]+j),p[i][j]=1LL*p[i][j]*pw%P;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<(1<<n);j++)
		{
			f[i][j]=1;
			for(int k=0;k<n;k++)
			{
				if(j>>k&1)
					f[i][j]=1LL*f[i][j]*p[k][i]%P;
				else
					f[i][j]=1LL*f[i][j]*(1+P-p[k][i])%P;
			}
		}
	}
	printf("%d",dfs(0,1));
}