LOJ 3119: 洛谷 P5400: 「CTS2019 | CTSC2019」隨機立方體


題目傳送門:LOJ #3119

題意簡述:

題目說的很清楚了。

題解:

記恰好有 \(i\) 個極大的數的方案數為 \(\mathrm{cnt}[i]\),則答案為 \(\displaystyle\frac{\mathrm{cnt}[k]}{(nml)!}\)

“恰好”這個詞非常的難受,我們考慮容斥:
\(\mathrm{f}[i]\) 為存在 \(i\) 個極大的數,且若恰好有 \(j\) 個極大的數,會被相應地統計 \(\displaystyle\binom{j}{i}\) 次的方案數。

則有:\(\displaystyle\mathrm{f}[i]=\sum_{j}\binom{j}{i}\mathrm{cnt}[j]\)

根據二項式反演,有:\(\displaystyle\mathrm{cnt}[i]=\sum_{j}(-1)^{j-i}\binom{j}{i}\mathrm{f}[j]\)

問題轉化為求出每一個 \(\mathrm{f}[i]\)


首先考慮以下事實:

  • 不會有超過 \(\min(n,m,l)\) 個極大的數。

  • 每兩個極大的數必然不可能有任意一維的坐標相同,這是因為每個極大的數需要大於所有與它有任意一維坐標相同的點上的數。

  • 若至少存在 \(i\) 個極大的數,則有 \(i\) 行,\(i\) 列和 \(i\) 個縱截面被至少一個極大的數“控制”。

所以,剩余的 \((n-i)(m-i)(l-i)\) 個數是沒有任何限制的,這是因為我們僅需保證至少存在 \(i\) 個極大的數。若剩下的數中又出現了極大的數也沒關系,這實際上並不在考慮范圍內。

又有,極大的數的條件僅和大小關系有關,而與數的絕對大小無關,所以可以看作乘上了一個“\(nml\) 取出 \((n-i)(m-i)(l-i)\) 個數進行排列的方案數”,即一個排列數的系數,而剩下的需要單獨考慮。

但是先別着急,我們先寫出目前得到的式子(下列式子中均將排列數寫作下降冪的形式):

\[\mathrm{f}[i]=\left(n^{\underline{i}}m^{\underline{i}}l^{\underline{i}}\right)\times\left((nml)^{\underline{(n-i)(m-i)(l-i)}}\right)\times\mathrm{g}[i] \]

第一個括號代表有順序地選出(即一個排列)\(i\) 個三維坐標均互不相同的點,這個順序即為之后的大小順序。

第二個括號代表對無關點進行標號,也是一個排列數。

\(\mathrm{g}[i]\) 代表在剩下的 \(nml-(n-i)(m-i)(l-i)\) 個點中按照規定的大小順序進行標號的方案數。


接下來我們考慮求出 \(\mathrm{g}[i]\)

讓我們先考慮 \(i\) 個極大值中最大的那個,它的位置已經被確定了,它的值也被確定為可選的值中的最大的那一個,而所有與它有關聯(有一維坐標相同)的位置都強制小於它。

但是,先等等!這些位置上的數,不一定只有最大值給予的一個限制,如果這個位置同時和其它極大值有關聯,那么不能單純用只最大值對其進行限制。

不過呢,其實這是沒有影響的,我們只需要管那些“只和最大值有關聯”的位置,若和其它極大值有關聯,我們放到之后再去處理即可。
從這里就可以看出先考慮最大值的高明之處,因為越小的極大值的限制越嚴格,我們采取從大到小考慮的方式,就可以只考慮最后的限制。

而考慮完最大值以及“只和最大值有關聯”的位置后,次大值的數值就應該為剩下的值中最大的那一個。

按照上述方式類推:對於最大的極大值,只要考慮那些和最大值有關聯的部分,但是必須去除和“比最大值小的極大值”有關聯的位置。
對於次大的,只要考慮和次大值有關聯的部分,但是要去掉和“比次大值小的極大值”有關聯的位置。
以此類推……

為了便於理解,我們展示 \(n=8,m=11,i=5\) 時的二維示例圖(你也可以理解為 \(l=1\) 的三維情況):

因為極大值的位置與答案無關,為了方便,按照從小到大的順序從左上角依次排下。

白色是無關位置,橙色是極大值所在位置,深綠色是僅和一個極大值有關聯的位置,黃綠色是同時和兩個極大值有關聯的位置。

對於不是無關位置的格子,寫上的數就表示它是在第幾輪被考慮的。

可以看出,深綠色部分在考慮對應的極大值時的同一輪時就被統計,而黃綠色部分須等到對應的較小的極大值的同一輪才會被統計。

這張圖的填數模式是淺顯易懂的,很簡單就能看出其中規律。

嘗試寫出其對應的 \(\mathrm{g}[i]\) 的值吧:\(\mathrm{g}[i]=69^{\underline{9}}\cdot 59^{\underline{11}}\cdot 47^{\underline{13}}\cdot 33^{\underline{15}}\cdot 17^{\underline{17}}\)
每個下降冪(排列數)就對應着相應的輪數,例如第一輪中是 \(69\) 個數選出 \(9\) 個來排列。

進一步地,我們嘗試寫出形式化的公式(為了方便表述,定義記號 \(\varrho(x)=(n-x)(m-x)(l-x)\)):
對於某個 \(i\) 值的第 \(i-j+1\) 輪,換句話說,也就是圖中從外往里數的第 \(j\) 層,
之前填完剩余的數的數量為 \(nml-(n-j)(m-j)(l-j)-1\) 個,即 \(\varrho(0)-\varrho(j)-1\)
需要填的位置的數量為 \((n-j+1)(m-j+1)(l-j+1)-(n-j)(m-j)(l-j)-1\) 個,即 \(\varrho(j-1)-\varrho(j)-1\)
所以就需要乘上:從“剩余的數”中取出“需要填的位置”那么多數進行排列以填入位置中的方案數,也就是一個排列數。

\(\displaystyle\mathrm{g}[i]=\prod_{j=1}^{i}(\varrho(0)-\varrho(j)-1)^{\underline{\varrho(j-1)-\varrho(j)-1}}\)
寫成階乘的形式就是 \(\displaystyle\mathrm{g}[i]=\prod_{j=1}^{i}\frac{(\varrho(0)-\varrho(j)-1)!}{(\varrho(0)-\varrho(j-1))!}\)
這時就一目了然了,不難化成如下形式:

\[\mathrm{g}[i]=(\varrho(0)-\varrho(i)-1)!\prod_{j=1}^{i-1}\frac{1}{\varrho(0)-\varrho(j)} \]


那么,將 \(\mathrm{g}[i]\) 代回 \(\mathrm{f}[i]\) 中吧!

\[\begin{aligned}\mathrm{f}[i]&=\left(n^{\underline{i}}m^{\underline{i}}l^{\underline{i}}\right)\times\left((nml)^{\underline{(n-i)(m-i)(l-i)}}\right)\times(\varrho(0)-\varrho(i)-1)!\prod_{j=1}^{i-1}\frac{1}{\varrho(0)-\varrho(j)}\\&=\left(n^{\underline{i}}m^{\underline{i}}l^{\underline{i}}\right)\frac{(nml)!}{(nml-\varrho(i))!}(nml-\varrho(i)-1)!\prod_{j=1}^{i-1}\frac{1}{nml-\varrho(j)}\\&=\left(n^{\underline{i}}m^{\underline{i}}l^{\underline{i}}\right)(nml)!\prod_{j=1}^{i}\frac{1}{nml-\varrho(j)}\end{aligned} \]

則答案為:\(\displaystyle\frac{\mathrm{cnt}[k]}{(nml)!}=\frac{1}{(nml)!}\sum_{i}(-1)^{i-k}\binom{i}{k}\mathrm{f}[i]\)

注意到和 \(\mathrm{f}[i]\) 里面的 \((nml)!\) 抵消掉了,干脆兩邊都不寫了吧:

\[\tilde{\mathrm{f}}[i]=\left(n^{\underline{i}}m^{\underline{i}}l^{\underline{i}}\right)\prod_{j=1}^{i}\frac{1}{nml-\varrho(j)} \]

\[\mathbf{Ans}=\sum_{i}(-1)^{i-k}\binom{i}{k}\tilde{\mathrm{f}}[i] \]


接下來就是最后的問題,我們需要在均攤 \(\mathcal{O}(1)\) 的時間內求出每個 \(\mathrm{f}[i]\)\(0\le k\le\min(n,m,l)\))。

可以發現瓶頸在於求逆元上,只要用到這題的方法就可以了。

下面是代碼:

#include <cstdio>

typedef long long LL;
const int Mod = 998244353;
const int MN = 5000005;

void exgcd(int a, int b, int &x, int &y) {
	if (!b) x = 1, y = 0;
	else exgcd(b, a % b, y, x), y -= a / b * x;
}
inline int Inv(int a) {
	int x, y;
	exgcd(a < 0 ? a + Mod : a, Mod, x, y);
	return x;
}

int Invs[MN];
inline void Init(int N) {
	Invs[1] = 1;
	for (int i = 2; i <= N; ++i)
		Invs[i] = -(LL)(Mod / i) * Invs[Mod % i] % Mod;
}

int N, M, L, Q, K, Ans;
int Vals[MN], iVals[MN];

inline int R(int x) { return (LL)(N - x) * (M - x) % Mod * (L - x) % Mod; }

int main() {
	Init(5000000);
	int T; scanf("%d", &T);
	while (T--) {
		scanf("%d%d%d%d", &N, &M, &L, &K), Ans = 0;
		Q = N < M ? N < L ? N : L : M < L ? M : L;
		iVals[0] = 1;
		for (int i = 1; i <= Q; ++i)
			Vals[i] = R(0) - R(i),
			iVals[i] = (LL)iVals[i - 1] * Vals[i] % Mod;
		int iV = Inv(iVals[Q]);
		for (int i = Q; i >= 1; --i)
			iVals[i] = (LL)iV * iVals[i - 1] % Mod,
			iV = (LL)iV * Vals[i] % Mod;
		int C = 0, S = 1;
		for (int i = 1; i <= Q; ++i) {
			S = (LL)S * R(i - 1) % Mod * iVals[i] % Mod;
			if (i == K) C = 1;
			if (i > K) C = -(LL)C * i % Mod * Invs[i - K] % Mod;
			Ans = (Ans + (LL)C * S) % Mod;
		}
		printf("%d\n", Ans < 0 ? Ans + Mod : Ans);
	}
	return 0;
}

感謝 @Winniechen 與我交流做法。


免責聲明!

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



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