题意
现在要给一个
n
∗
m
∗
l
n*m*l
n∗m∗l的矩形的每一位等概率地填上
1
1
1到
n
∗
m
∗
l
n*m*l
n∗m∗l中的一个数,且满足每个数恰好出现一次。定义一个位置为极大值当且仅当所有坐标至少有一维和他相同的位置的数都比他小。问恰好有
k
k
k个极大值的概率。
n
,
m
,
l
≤
5000000
,
k
≤
100
n,m,l\le5000000,k\le100
n,m,l≤5000000,k≤100
分析
考虑容斥,那么就要对每个
k
≤
i
≤
m
i
n
(
n
,
m
,
l
)
k\le i\le min(n,m,l)
k≤i≤min(n,m,l)算出至少有
i
i
i个极大值的答案。
假设当前我们已经钦定了这
i
i
i个极大值的位置,显然他们每一维的坐标互不相同。
现在只用考虑至少有一维坐标和这
i
i
i个位置中的某一个相同的那些位置,显然合法的位置有
n
m
l
−
(
n
−
i
)
(
m
−
i
)
(
l
−
i
)
nml-(n-i)(m-i)(l-i)
nml−(n−i)(m−i)(l−i)个。
如果我们把这
i
i
i个数按照从大到小的顺序填入,那么最大的数必然是这些位置中的最大值,概率为
1
n
m
l
−
(
n
−
i
)
(
m
−
i
)
(
l
−
i
)
\frac{1}{nml-(n-i)(m-i)(l-i)}
nml−(n−i)(m−i)(l−i)1
填好最大的数后,和其他
i
−
1
i-1
i−1个格子没有坐标相同的那
(
n
−
i
+
1
)
(
m
−
i
+
1
)
(
l
−
i
+
1
)
−
(
n
−
i
)
(
m
−
i
)
(
l
−
i
)
(n-i+1)(m-i+1)(l-i+1)-(n-i)(m-i)(l-i)
(n−i+1)(m−i+1)(l−i+1)−(n−i)(m−i)(l−i)个格子无论怎么填都必然合法。
同理,第二大的数必然是剩下的
n
m
l
−
(
n
−
i
+
1
)
(
m
−
i
+
1
)
(
l
−
i
+
1
)
nml-(n-i+1)(m-i+1)(l-i+1)
nml−(n−i+1)(m−i+1)(l−i+1)个位置中的最大值,概率为
1
n
m
l
−
(
n
−
i
+
1
)
(
m
−
i
+
1
)
(
l
−
i
+
1
)
\frac{1}{nml-(n-i+1)(m-i+1)(l-i+1)}
nml−(n−i+1)(m−i+1)(l−i+1)1
所以答案就是
∑
i
=
k
m
i
n
(
n
,
m
,
l
)
(
−
1
)
i
−
k
C
i
k
A
n
i
A
m
i
A
l
i
∏
j
=
1
i
1
n
m
l
−
(
n
−
j
)
(
m
−
j
)
(
l
−
j
)
\sum_{i=k}^{min(n,m,l)}(-1)^{i-k}C_i^kA_n^iA_m^iA_l^i\prod_{j=1}^i\frac{1}{nml-(n-j)(m-j)(l-j)}
i=k∑min(n,m,l)(−1)i−kCikAniAmiAlij=1∏inml−(n−j)(m−j)(l−j)1
时间复杂度
O
(
n
)
O(n)
O(n)。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
typedef long long LL;
const int N=5000005;
const int MOD=998244353;
int n,m,l,k,jc[N],ny[N],pre[N];
int ksm(int x,int y)
{
int ans=1;
while (y)
{
if (y&1) ans=(LL)ans*x%MOD;
x=(LL)x*x%MOD;y>>=1;
}
return ans;
}
int C(int n,int m)
{
return (LL)jc[n]*ny[m]%MOD*ny[n-m]%MOD;
}
int A(int n,int m)
{
return (LL)jc[n]*ny[n-m]%MOD;
}
void solve()
{
jc[0]=jc[1]=ny[0]=ny[1]=1;
for (int i=2;i<=l;i++) jc[i]=(LL)jc[i-1]*i%MOD,ny[i]=(LL)(MOD-MOD/i)*ny[MOD%i]%MOD;
for (int i=2;i<=l;i++) ny[i]=(LL)ny[i-1]*ny[i]%MOD;
pre[n]=1;
for (int i=1;i<=n;i++) pre[n]=(LL)((LL)n*m%MOD*l%MOD+MOD-(LL)(n-i)*(m-i)%MOD*(l-i)%MOD)*pre[n]%MOD;
pre[n]=ksm(pre[n],MOD-2)%MOD;
for (int i=n;i>1;i--) pre[i-1]=(LL)((LL)n*m%MOD*l%MOD+MOD-(LL)(n-i)*(m-i)%MOD*(l-i)%MOD)*pre[i]%MOD;
int ans=0;
for (int i=k;i<=n;i++)
{
int w=(LL)C(i,k)*A(n,i)%MOD*A(m,i)%MOD*A(l,i)%MOD*pre[i]%MOD;
if ((i-k)&1) (ans+=MOD-w)%=MOD;
else (ans+=w)%=MOD;
}
printf("%d\n",ans);
}
int main()
{
int T;scanf("%d",&T);
while (T--)
{
scanf("%d%d%d%d",&n,&m,&l,&k);
if (n>m) std::swap(n,m);
if (n>l) std::swap(n,l);
if (m>l) std::swap(m,l);
solve();
}
return 0;
}