XJOI1105模擬賽 積木遊戲
阿新 • • 發佈:2020-11-05
orz duck!
Description
在一個 \(n\) 行 \(m\) 列的矩陣裡放積木。要求:一塊積木只能放在一行,一行中相鄰的積木中間至少有一個間隔,第一行至少有一塊積木,第 \(n\) 行積木長度為 \(m\)。求所有滿足條件的方案的積木數平方和。
Solution
注意到原題範圍 \(n,m\leq 5\times 10^3\) 太小了,考慮加強 \(\rightarrow~n,m\leq 10^{18}\)。
首先把這個第一行至少有一塊積木的限制去掉,記 \(f_n\) 表示最高積木高度小於等於 \(n\) 的積木數平方和,則答案即為 \(f_n - f_{n-1}\)。
對於一個積木的高度序列 \(\{a_m\}\)
把那個 \(\max\) 拆開,得到:
\[f_n = n^m\operatorname E\left(\sum_i\sum_j\max\{a_i-a_{i-1},0\}\max\{a_j-a_{j-1},0\}\right) \]然後分類討論 \(i,j\) 的情況,分 \(i=j\),\(|i-j|=1\),\(|i-j|>1\) 討論,其中 \(i=1\) 或 \(j=1\) 還要進一步討論。
注意到每種情況都是一個關於 \(n\) 的低次多項式,可以使用拉格朗日插值減少計算。
Code
(整數類模板省略)
const int MOD = 998244353; inline Z suan1(Z N) { return N * 166374059 + N * N * 499122177 + (N ^ 3) * 332748118; } inline Z suan2(Z N) { return N * N * 415935147 + (N ^ 4) * 582309206; } inline Z suan3(Z N) { return N * 415935147 + N * N * 707089750 + (N ^ 3) * 582309206 + (N ^ 4) * 291154603; } inline Z suan4(Z N) { return N * N * 415935147 + (N ^ 3) * 415935147 + (N ^ 4) * 582309206 + (N ^ 5) * 582309206; } inline Z suan5(Z N) { return N * 432572553 + (N ^ 3) * 707089750 + (N ^ 5) * 856826403; } inline Z suan6(Z N) { return N * N * 859599304 + (N ^ 4) * 277290098 + (N ^ 6) * 859599304; } int n, m; inline Z calc(int n, int m) { if (!n) return Z(0); if (n == 1) return Z(1); Z N(n), M(m), ans = suan1(N) / N; if (m > 1) ans += (M - 1) * suan2(N) / (N * N); if (m > 1) ans += 2 * suan3(N) / (N * N); if (m > 2) ans += 2 * (M - 2) * suan4(N) / (N ^ 3); if (m > 2) ans += 2 * (M - 2) * suan5(N) / (N ^ 3); if (m > 3) ans += (M - 2) * (M - 3) * suan6(N) / (N ^ 4); return ans; } int main() { scanf("%d%d", &m, &n); Z ans = calc(n, m) * (Z(n) ^ m) - calc(n - 1, m) * (Z(n - 1) ^ m); printf("%d\n", ans.x); return 0; }
事實上如果真的要做 \(n,m\leq 10^{18}\) 這份程式碼還要作億點改動(因為大於模數了)但是由於懶就不想管了