给定 和一个 的非负整数矩阵 ,。
根据这个矩阵构造一个二分图,其有 个左部点和 个右部点,连接第 个左部点和第 个右部点的无向边 有 的概率存在。
求这个二分图的最大匹配大小的期望。
。
考虑 Hall 定理,一个二分图的最大匹配大小为 ,其中 是左部点集 的邻域。
问题是取到最大值的 可能有多个。
勇敢地去考虑代表元,不妨令代表元为 最大且 最小的左部点集 。可以证明这是唯一的:
若有两个左部点集 满足 且 ,则:
这是因为 且 。
那么 和 中的某一个集合一定能优于 和 ,矛盾。
那么就可以 dp 了,设 表示只考虑左部点集 和右部点集 的子图时, 且 是子图代表元的概率; 表示只考虑 的子图时, 且最大匹配大小为 的概率。
转移考虑容斥,枚举实际的代表元 ,那么 和 的导出子图的最大匹配大小显然为 ,故有转移:
其中 表示只考虑 的子图时 的概率, 表示 之间没有边的概率。
计算答案是简单的,枚举整个图的代表元 和 即可。
时间复杂度 ,代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
#define p 998244353
inline void add(int &x,int y)
{
x+=y;
if(x>=p) x-=p;
}
inline int qpow(int x,int y)
{
int res=1;
for(;y>0;y>>=1,x=1ll*x*x%p) res=y&1?1ll*res*x%p:res;
return res;
}
const int S=15,BS=1<<9;
int n,m,a[S][S];
int noe[BS][BS],h[BS][BS],f[BS][BS],g[BS][BS];
#define popc(x) __builtin_popcount(x)
inline void slove()
{
cin>>n>>m;
int iv=qpow(100,p-2);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>a[i][j],a[i][j]=1ll*a[i][j]*iv%p;
for(int s=0;s<(1<<n);s++)
for(int t=0;t<(1<<m);t++)
noe[s][t]=h[s][t]=f[s][t]=g[s][t]=0;
for(int s=0;s<(1<<n);s++)
for(int t=0;t<(1<<m);t++)
{
noe[s][t]=1;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if((s>>i&1)&&(t>>j&1))
noe[s][t]=1ll*noe[s][t]*(1-a[i][j]+p)%p;
}
for(int s=0;s<(1<<n);s++)
for(int t=0;t<(1<<m);t++)
{
h[s][t]=1;
for(int j=0;j<m;j++)
{
if(t>>j&1^1) continue;
int pre=1;
for(int i=0;i<n;i++)
if(s>>i&1) pre=1ll*pre*(1-a[i][j]+p)%p;
h[s][t]=1ll*h[s][t]*(1-pre+p)%p;
}
}
for(int s=0;s<(1<<n);s++)
for(int t=0;t<(1<<m);t++)
if(popc(s)<=popc(t))
{
f[s][t]=h[s][t];
for(int s1=s;;s1=(s1-1)&s)
{
for(int t1=t;;t1=(t1-1)&t)
{
if(s1!=0||t1!=0)
add(f[s][t],p-1ll*g[s1][t1]*f[s-s1][t-t1]%p
*noe[s1][t-t1]%p);
if(t1==0) break;
}
if(s1==0) break;
}
}
else
{
g[s][t]=h[s][t];
for(int s1=s;;s1=(s1-1)&s)
{
for(int t1=t;;t1=(t1-1)&t)
{
if((s1!=s||t1!=t)
&&popc(s1)-popc(t1)>=popc(s)-popc(t))
{
add(g[s][t],p-1ll*g[s1][t1]*f[s-s1][t-t1]%p
*noe[s1][t-t1]%p);
}
if(t1==0) break;
}
if(s1==0) break;
}
}
int ans=0;
int alln=(1<<n)-1,allm=(1<<m)-1;
for(int s=0;s<(1<<n);s++)
for(int t=0;t<(1<<m);t++)
if(popc(s)>popc(t))
{
int lst=allm-t;
for(int x=lst;;x=(x-1)&lst)
{
add(ans,1ll*g[s][t]*f[alln-s][x]%p
*noe[s][allm-t]%p*noe[alln-s][lst-x]%p
*(popc(s)-popc(t))%p);
if(x==0) break;
}
}
cout<<(n-ans+p)%p<<'\n';
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int T;
cin>>T;
while(T-->0) slove();
return 0;
}