1. 程式人生 > 其它 >Petya and Coloring 題解

Petya and Coloring 題解

link
手玩一下,發現隨著線右移,線左邊的顏色總數不遞減,右邊的顏色總數不遞增,那麼可得出最左邊一列的顏色總數等於最右邊一列的顏色總數,且 中間的顏色必須是旁邊兩列顏色交集的子集
由於這條分割線在最左邊或者最右邊的時候是兩個極端,所以就只用考慮這兩個位置。換句話說,中間的 \(m-2\) 行可以看作是一行。(trick)

於是我們列舉兩邊的兩列顏色的總數 \(i\),再列舉旁邊兩列顏色交集的個數 \(j\),顯然選出兩列顏色的方案數為 \(\binom {k} {i}\binom {i} {j}\binom {k-i} {i-j}\),中間的顯然可以用排列組合算 (\(j^{(m-2)*n}\)

)。兩邊的就變成了:有 \(n\) 個點,有 \(i\) 個顏色,要求每種顏色都用到的方案數。(這其實就是第二類 Stirling 數。)

現在,我們發現恰好用到 \(i\) 種顏色不好維護,不妨探究一下至少用到和恰好用到方案數的關係,令至少用到 \(i\) 種顏色方案數其 \(F_i=\binom {n} {i}*i^{n-i}\)。注意這裡的 \(F\) 會有重複方案,但是讓我們繼續往下思考,因為這就是二項式反演的一種通用形式link)。

你會發現 \(F_i=\sum_{k=i}^n\binom {k} {i}f_k\),這裡需要想一會。含義為 \(i\) 個顏色能拓展到 \(k\) 個算一種方案,那麼選出 \(i\)

個顏色的方案數就是 \(\binom k i\)。容斥也是這樣思考的。

那麼直接二項式反演算即可,最後 \(\mathrm {ans}=\sum_{i=1}^n\sum_{j=1}^if_i^2\binom {k} {i}\binom {i} {j}\binom {k-i} {i-j}j^{(m-2)*n}=\sum_{i=1}^nf_i^2\sum_{j=1}^i\binom {k} {i}\binom {i} {j}\binom {k-i} {i-j}j^{(m-2)*n}\),其中 \(f_i=\sum_{k=i}^n(-1)^{i-k}\binom {k} {i}\binom n k k^{n-k}\)

\(\mathrm {ans}=\sum_{i=1}^n(\sum_{k=i}^n(-1)^{i-k}\binom {k} {i}\binom n k k^{n-k})^2\sum_{j=1}^i\binom {k} {i}\binom {i} {j}\binom {k-i} {i-j}j^{(m-2)*n}\)。預處理一下,時間複雜度:\(\mathcal {O(n^2)}\)

值得一提的是,這裡的 \(f_i\) 可以直接 \(\mathrm {dp}\) 轉移。具體的轉移也不是很難。
還有一個細節是 \(m=1\)\(m=2\) 的時候最好特判一下。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define LL long long
#define int long long
using namespace std;
const int MAXN = 1e6 + 5, Mod = 1e9 + 7;
int n, m, k;
LL jc[MAXN], inv[MAXN], dp[1005][1005];
LL Qpow(LL x, int y) {
	LL ans = 1;
	for(; y; y >>= 1) {
		if(y & 1) ans = ans * x % Mod;
		x = x * x % Mod;
	}
	return ans;
}
LL C(int x, int y) {
	if(x < 0 || y < 0 || x < y) return 0;
	return jc[x] * inv[y] % Mod * inv[x - y] % Mod;
}
LL QWQ(int x) {
//	if(!x) return 0;
//	return (Qpow(x, n) - Qpow(x - 1, n)) % Mod;
	return dp[n][x] * jc[x] % Mod;
	LL ans = 0;
	for(int i = 0; i <= x; i ++) ans = (ans + Qpow(Mod - 1, x - i) * C(x, i) % Mod * Qpow(i, n)) % Mod;
	return ans;
}
signed main() {
	LL ans = 0; 
	scanf("%lld%lld%lld", &n, &m, &k); jc[0] = 1;
	int K = k; k = min(k, n);
	for(int i = 1; i <= K; i ++) jc[i] = jc[i - 1] * i % Mod;
	inv[K] = Qpow(jc[K], Mod - 2);
	for(int i = K - 1; i >= 0; i --) inv[i] = inv[i + 1] * (i + 1) % Mod;
	dp[0][0] = 1;
	for(int i = 1; i <= n; i ++) {
		for(int j = 1; j <= i; j ++) dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j] * j) % Mod;
	}
	if(m == 1) {
		for(int i = 0; i <= k; i ++) ans = (ans + Qpow(QWQ(i) * C(K, i) % Mod, m)) % Mod;
		printf("%lld", ans); return 0;
	}
	if(m == 2) {
		for(int i = 0; i <= k; i ++) ans = (ans + Qpow(QWQ(i) % Mod, m) * C(K, i) % Mod * C(K, i)) % Mod;
		printf("%lld", ans); return 0;
	}
	for(int i = 1; i <= n; i ++) {
		for(int j = 1; j <= i; j ++) {
			ans = (ans + QWQ(i) * QWQ(i) % Mod * C(K, i) % Mod * C(K - i, i - j) % Mod * C(i, j) % Mod * Qpow(j, (m - 2) * n)) % Mod; 
		}
	}
	//for(int i = 0; i <= k; i ++) printf("|%lld %lld|\n", Qpow(QWQ(i) % Mod, m), C(K, i)), ans = (ans + Qpow(QWQ(i) % Mod, m) * C(K, i)) % Mod;
	printf("%lld", ans);	
	return 0;
}