题解 P5400 【[CTS2019]随机立方体】
command_block · · 题解
题目Link:P5400 [CTS2019]随机立方体
MC大法好!MC造图解此题!
有一个
将
多组数据。
设
首先这个恰好非常难受,我们按照套路采用重复统计,即:首先弄出
容易得到:没有两个级大数会在同一个平面上,所以最多有
这里要特判一下,如果
那么就能得到
解释一下为什么:如果一个方案中有
所以这里要再次声明:所谓的
(这个错误好像大多数题解都有犯过,我初学的时候一直认为
只用求
问题在于如何求出
为了方便,这里设
第一个级大数放置之后,有三个平面的数必须小于该数,其他的位置并没有影响。
所以可选的位置变成了
那么总的方案数是
- 被级大数影响的数的填写方法
本题最麻烦的东西。
这
设
我们先要选出
此外,我们还要考虑这
第一个级大数放置之后,有三个平面的数必须小于该数,其他的位置并没有影响。
第二个级大数放置之后,有三个平面的数必须小于该数,其他的位置并没有影响。
问题在于这六个平面有交,那样子是无法分析的。
不过我们发现,假如第一个数小于第二个数的话,那么第一个数控制的三个平面必然都小于第二个数,也就是说,没有影响。
容易发现,对于一种合法方案,我们可以任意交换两个面,交换后仍然合法。
那么我们前面已经钦定了
由于我们在这里钦定了极大值的相对大小,所以这里还要乘上
彩色羊毛表示极大值,玻璃表示这些极大值的控制范围。
那么如图,对应颜色玻璃玻璃就是该颜色羊毛的控制区域。
设
那么,对于蓝色的区域,它们只需要满足小于蓝色羊毛就好了。
我们可以容易地算出每个区域的大小,比如蓝色区域的大小是
为了方便,我们设
那么第
我们把这个东西从里到外一层一层剥开来理解:
显而易见的,值
这里
我们知道
那么方案总数是
(z这里之所以减1是因为我们钦定了一个数作为极大值)
对于下一层,可以看做变成了
所以总方案数是
这部分的总方案数乘起来就是
- 其他的数的填法
放任自流就好了,剩下
我们把公式汇总一下:
因为我们这里算的是方案数,这里除以
那么到了这里,你会注意到
我们发现式子的前面有一个
由于
我们发现这个东西十分的简洁,不过我们对于
求出
总复杂度
由于这题时间限制很松,我就
#include<algorithm>
#include<cstdio>
#define mod 998244353
#define MaxN 5000500
using namespace std;
long long powM(long long a,long long t=mod-2)
{
a=(a%mod+mod)%mod;
long long ans=1;
while(t){
if(t&1)ans=(ans*a)%mod;
a=(a*a)%mod;
t>>=1;
}return ans;
}
long long fac[MaxN],inv[MaxN];
void Init()
{
fac[0]=fac[1]=inv[0]=inv[1]=1;
for (int i=2;i<=5000000;i++){
fac[i]=fac[i-1]*i%mod;
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}for (int i=2;i<=5000000;i++)
inv[i]=inv[i]*inv[i-1]%mod;
}
long long C(int n,int m)
{return fac[n]*inv[n-m]%mod*inv[m]%mod;}
int F[MaxN],G[MaxN],Gl[MaxN],R,n,m,l,k,T;
inline int g(int k)
{return 1ll*(n-k)*(m-k)%mod*(l-k)%mod;}
int main()
{
Init();Gl[0]=1;
int qt;
scanf("%d",&qt);
while(qt--){
scanf("%d%d%d%d",&n,&m,&l,&k);
R=min(n,min(m,l));
if (k>R){printf("0\n");continue;}
T=1ll*n*m%mod*l%mod;
for (int i=1;i<=R;i++)
G[i]=powM((T-g(i)+mod)%mod);
long long buf=1;
for (int i=1;i<=R;i++)
F[i]=buf=buf*g(i-1)%mod*G[i]%mod;
buf=0;
for (int i=k;i<=R;i++)
if ((i-k)&1)
buf=(buf-C(i,k)*F[i])%mod;
else buf=(buf+C(i,k)*F[i])%mod;
printf("%lld\n",(buf+mod)%mod);
}return 0;
}