P5400 [CTS2019] 随机立方体 题解
题目描述
一个 \(n\times m\times l\) 的立方体,现在要将数字 \(1\sim nml\) 分别填到立方体的每个格子中。
如果一个格子中的数比其他三维坐标至少一维相同的格子上的数都要大,那么称这个格子是极大的。
\(T\) 组询问,给定 \(n,m,l\) ,求恰好有 \(k\) 个极大的格子的概率,对 \(998244353\) 取模。
数据范围
- \(1\le T\le 10,1\le n,m,l\le 5\cdot 10^6,1\le k\le 100\) 。
时间限制 \(\texttt{12s}\) ,空间限制 \(\texttt{500MB}\) 。
分析
先求方案数,最后除以 \((nml)!\) 就是概率。
看到 "恰好" 二字,二项式反演跑不了。问题转化为求钦定 \(k\) 个位置是极大值的方案数,记为 \(g_k\) 。
不妨这 \(k\) 个极大值点为 \((1,1,1),\cdots,(k,k,k)\) ,并且 \(w_{1,1,1}\le\cdots\le w_{k,k,k}\) 。
先考虑这个 "不妨" 的系数。由于每个平面至多一个极大值点,因此 \(w_{1,1,1}\) 有 \(nml\) 种取法, \(w_{2,2,2}\) 有 \((n-1)(m-1)(l-1)\) 种取法,依次类推。
可以看到这些极大值 "支配" 了一些点,而其余位置(三维坐标均 \(\ge k+1\))是无关的。
先把无关的位置都填好,方案数 \(nml^\underline{(n-k)(m-k)(l-k)}\) 。接下来只关心剩下的数的相对大小关系。
记 \(c_i\) 为前 \(i\) 层的点数,容易得到 \(c_i=nml-(n-i)(m-i)(l-i)\) 。
记 \(f_i\) 为将 \(1\sim c_i\) 填给前 \(i\) 层的方案数。
至此,我们可以得到:
问题转化为计算 \(f_i\) 。
容易发现仅被极大值点 \((i,i,i)\) "支配" 的点数为 \(c_i-c_{i-1}-1\),最后的 \(-1\) 是因为不算自身。
显然 \(w_{i,i,i}=c_i\) ,然后被 \((i,i,i)\) "支配" 的点没有其他限制,方案数 \((c_i-1)^{c_i-c_{i-1}-1}\) 。
剩余部分是一个 \(i-1\) 阶子问题,方案数为 \(f_{i-1}\) 。
综上可得递推式 \(f_i=(c_i-1)^\underline{c_i-c_{i-1}-1}\cdot f_{i-1}\) ,边界 \(f_0=1\) 。
累乘求 \(f_k\) :
再把 \(f_k\) 带回 \(g_k\) 的式子,注意不要忘记 \(c_i\) 的定义:
显然可以递推,注意需要线性处理 \(c_i\) 前缀积的逆元。
最后二项式反演,由于求的是概率所以还要除以 \((nml)!\) 。
刚好把 \((nml)!\) 消掉,时间复杂度 \(\mathcal O\big(T\min(n,m,l)\big)\) 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5e6+5,mod=998244353;
int k,l,m,n,t,v,cur,res;
int c[maxn],g[maxn],fac[maxn],inv[maxn];
int qpow(int a,int k)
{
int ans=1;
while(k)
{
if(k&1) ans=ans*a%mod;
a=a*a%mod,k/=2;
}
return ans;
}
void init(int n)
{
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
inv[n]=qpow(fac[n],mod-2);
for(int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
int get_c(int n,int m)
{
if(m<0||n<m) return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
signed main()
{
scanf("%lld",&t),init(maxn-5);
while(t--)
{
scanf("%lld%lld%lld%lld",&n,&m,&l,&k);
v=min(min(n,m),l),cur=g[0]=1;
for(int i=1;i<=v;i++)
{
c[i]=(n*m%mod*l-(n-i)*(m-i)%mod*(l-i))%mod;
cur=cur*c[i]%mod;
g[i]=g[i-1]*(n-i+1)%mod*(m-i+1)%mod*(l-i+1)%mod;
}
cur=qpow(cur,mod-2),res=0;
for(int i=v;i>=k;i--)
{
g[i]=g[i]*cur%mod,cur=cur*c[i]%mod;
res=(res+(i-k&1?-1:1)*get_c(i,k)*g[i])%mod;
}
printf("%lld\n",(res+mod)%mod);
}
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16375080.html
浙公网安备 33010602011771号