E. Assimilation IV
1)将矩阵保存为a[i][j]
2)对于每一列,考虑它对答案的贡献
依题意,对于n!种排列中的一种p,对于第j列,
如果有一行i,使得p[i]>=a[i][j],那么这一列对答案贡献1
3)可以反过来考虑什么情况下第j列不贡献,那就是每一个i都有p[i]<a[i][j]
4)方案计数:
对于第j列,枚举i
表示当前对1~i-1进行排列,sum表示前面已经排列了sum个,
tmp为当前的计数,cnt[i]表示当前列a值为i的行的个数
那么更新tmp:tmp=C(cnt[i],i-1-sum)*(cnt[i]!)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod=998244353,maxn=5e4+5;
int a[25][maxn];
ll tmp,ans,cnt[25],sum,ss,jie[25],ni[25];
ll pow_m(ll x,int k)
{
ll sum=1;
while(k){
if(k&1)sum=sum*x%mod;
x=x*x%mod; k>>=1;
}
return sum;
}
ll C(int m,int n)
{
return jie[n]*ni[m]%mod*ni[n-m]%mod;
}
int main()
{
int n,m,x,flag;
scanf("%d%d",&n,&m);
jie[0]=1; ni[0]=1;
for(int i=1;i<=n;i++){
jie[i]=jie[i-1]*i%mod;
ni[i]=pow_m(jie[i],mod-2);
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
}
}
for(int j=1;j<=m;j++){
for(int i=1;i<=n+1;i++)cnt[i]=0;
for(int i=1;i<=n;i++){
cnt[a[i][j]]++;
}
flag=1; sum=0;
for(int i=1;i<=n+1;i++){
sum+=cnt[i];
if(sum>=i){
flag=0; break;
}
}
tmp=1;
if(flag){
sum=0;
for(int i=1;i<=n+1;i++){
tmp=tmp*C(cnt[i],i-1-sum)%mod*jie[cnt[i]]%mod;
sum+=cnt[i];
}
tmp=(jie[n]-tmp+mod)%mod;
}
else tmp=jie[n];
// cout<<j<<' '<<tmp<<endl;
ans=(ans+tmp)%mod;
}
printf("%lld\n",ans*ni[n]%mod);
return 0;
}