【2020.9.12 NOIP模擬賽 T3】普及組
求矩陣的所有行和列的乘積均為 \(k\) 的方案數。要求矩陣是 \(n \times n\) 的,由整陣列成(可負)。\(T \le 2 \times 10^5,n \le 5 \times 10^6\),每組資料的 \(k\) 已知,質因數不超過二次。
首先符號的貢獻顯然是獨立的,\(k=1\) 的貢獻顯然是 \(2^{(n-1)^2}\)(考慮前 \((n-1) \times (n-1)\) 隨便填,用剩下一行一列來調整)。那麼剩下我們要做的就是計算出正整數的貢獻。
考慮到 \(k = 31 \times 37^2\) 與 \(k = 2 \times 3^2\) 等價。具體地說,答案只與 \(k\)
首先考慮一次質因數的情況。那麼我們要做的就是在 \(n \times n\) 的網格中每行每列都只填一個 1 的方案數。這是個組合數學的入門題,答案是 \(n!\)
然後考慮二次質因數的情況。那麼我們要做的就是在 \(n \times n\) 的網格中填若干 \(1\) 或 \(2\),使得每行每列的和為 \(2\)。題解中有一個轉化為圖論問題用生成函式多項式求逆求解的方法,並不會。
考慮最後一列填的什麼。如果填的是 \(2\),那麼刪去最後一列和那一行能夠轉化成子問題;如果填的是兩個 \(1\),那麼如果這兩行的另一個 \(1\)
設 \(f(n)\) 表示 \(n \times n\) 矩陣最右邊列為一個 \(2\) 的方案數;\(g(n)\) 表示 \(n \times n\) 矩陣最右邊列是兩個 \(1\) 的方案數。那麼轉移為:
\[f(n) \gets n(f(n-1)+g(n-1)) \]
\[g(n) \gets {n \choose 2} (f(n-1)+2g(n-1)) \]
\[Ans(n) \gets f(n)+g(n) \]
關鍵程式碼
inline void init() {
f[1] = 0, g[1] = 1;
for (register int i = 2; i <= 5e6; ++i) {
g[i] = 1ll * i * (g[i - 1] + f[i - 1]) % P;
f[i] = (1ll * i * (i - 1) / 2) % P * ((f[i - 1] << 1) + g[i - 1]) % P;
}
}
int main() {
...
init();
int _; read(_);
while (_--) {
int n; read(n);
ll ans = quickpow(jie[n], can1) * quickpow((f[n] + g[n]) % P, can2) % P;
ans = ans * quickpow(2, 1ll * (n - 1) * (n - 1) % (P - 1)) % P;
printf("%lld\n", (ans % P + P) % P);
}
return 0;
}