【CTS2019】隨機立方體(容斥)


【CTS2019】隨機立方體(容斥)

題面

LOJ
洛谷

題解

做這道題目的時候不難想到容斥的方面。
那么我們考慮怎么計算至少有\(k\)個極大值的方案數。
我們首先可以把\(k\)個極大值的位置給確定出來,方案數是\(\displaystyle {n\choose k}{m\choose k}{l\choose k}(k!)^3\),乘上\(k!\)是為了確定之間的順序關系,即我們先確定\(xyz\)三維,然后把這三維要一一對應到點才行。假設這個值是\(w[k]\)
剩下要填的是兩個部分,一個是剩下的\((n-k)(m-k)(l-k)\)個沒有什么影響的位置,以及和極大值有至少一維坐標相交的點。
所以方案數大概可以寫成\({nml\choose nml-(n-k)(m-k)(l-k)}w[k]((n-k)(m-k)(l-k))!*h[k]\)的樣子。
其中\(h[k]\)是分配極大值所在的\(k*k*k\)的這個小立方體上的數字的方案數。
為了方便\(V=nml,v[k]=V-(n-k)(m-k)(l-k)\)
所以我們只需要考慮怎么分配這個挖掉\(k\)層的合法方案數。
首先每次確定掉一個極大值之后,我們就可以把它所在的這三個面直接丟掉,變成小一圈的一個立方體,而對於極大值而言,因為它要比所有同層的數都要大,所以我們從大往小,從內往外考慮填數。
首先最值的位置一定是三個面的交點,並且最值一定是當前所有剩余可填的數中的最大值。所以只需要確定剩下的數的數就行了,這個時候還有\(v[i]-1\)個數可以選,要選\(v[i]-v[i-1]-1\)個數,所以填進去的方案數就是\(\frac{(v[i]-1)!}{v[i-1]!}\)
所以\(h[k]=h[k-1]*\frac{(v[k]-1)!}{v[k-1]!}\)
所以\(\displaystyle h[k]=\prod_{i=1}^{k} \frac{(v[i]-1)!}{v[i-1]!}\)
然后把答案式掏出來,是:

\[\begin{aligned} &\ \ \ \ \displaystyle {V\choose v[k]}w[k](V-v[k])!h[k]\\ &=\frac{V!}{v[k]!}w[k]h[k]\\ &=V!\frac{1}{v[k]!}w[k]h[k]\\ &=V!\frac{1}{v[k]!}w[k]\prod_{i=1}^k(v[i]-1)!\prod_{i=0}^{k-1}\frac{1}{v[i]!}\\ &=V!w[k]\prod_{i=1}^k \frac{1}{v[i]} \end{aligned}\]

然后題目要求的是概率,所以和\(V!\)就沒有關系了。
那么就只有后半部分。
這樣子就很容易計算了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 5001000
#define MOD 998244353
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int jc[MAX],jv[MAX],inv[MAX];
int n,m,l,V,M,k,ans;
int v[MAX],w[MAX],s[MAX],invs[MAX];
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
int C(int n,int m){return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int main()
{
	jc[0]=jv[0]=inv[0]=inv[1]=1;
	for(int i=2;i<MAX;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<MAX;++i)jc[i]=1ll*jc[i-1]*i%MOD;
	for(int i=1;i<MAX;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
	int T=read();
	while(T--)
	{
		n=read();m=read();l=read();k=read();V=1ll*n*m%MOD*l%MOD;M=min(min(n,m),l);ans=0;
		for(int i=1;i<=M;++i)v[i]=(V-1ll*(n-i)*(m-i)%MOD*(l-i)%MOD+MOD)%MOD;
		for(int i=1;i<=M;++i)w[i]=1ll*C(n,i)*C(m,i)%MOD*C(l,i)%MOD*jc[i]%MOD*jc[i]%MOD*jc[i]%MOD;
		s[0]=1;for(int i=1;i<=M;++i)s[i]=1ll*s[i-1]*v[i]%MOD;
		invs[M]=fpow(s[M],MOD-2);for(int i=M-1;i;--i)invs[i]=1ll*invs[i+1]*v[i+1]%MOD;
		for(int i=k,d=1;i<=M;++i,d=MOD-d)ans=(ans+1ll*d*C(i,k)%MOD*w[i]%MOD*invs[i])%MOD;
		printf("%d\n",ans);
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM