2020CCPC秦皇島 H. Holy Sequence
阿新 • • 發佈:2020-10-22
考慮每個數字 \(x\) 第一次出現在位置 \(p\) 的貢獻, 那麼就要計算 \(x\) 前後的合法序列的數量了.
定義這兩個 \(dp\) 陣列:
- \(pre[i][j]\): 當 \(n=i\) 時, \(p_n=j\) 的合法序列數.
- \(suff[i][j]\): \(a_1=j\) (無視 \(a_0\) 的限制, 值域相應\(+j\)) 長度為 \(i\) 的合法序列數.
這兩個陣列的遞推還是好求的, 與第二類斯特林數很像.
然後我們考慮平方怎麼處理. 對於 \(x\) 出現了 \(k\) 次的序列, 我們相當於可重複地選兩次 \(x\). 也算個經典技巧.
現在列舉每個數字 \(i\)
- 兩次都選擇位置 \(j\).
- 兩次選擇位置相同但不是 \(j\).
- 兩次中一次選擇 \(j\), 一次不選 \(j\).
- 兩次選擇位置不同且都不選擇 \(j\).
每次選擇後面有 \(0-2\) 個 \(i\) 時, 相當於這個位置被佔用了, 可以直接去掉, 剩的位置方案即 \(suff[n-j-x][i]\).
程式碼
#include<bits/stdc++.h> using namespace std; #define ll long long #define inc(x, l, r) for(int x = l; x <= r; x++) const int N = 3e3; int t, n, m; ll pre[N + 5][N + 5], suff[N + 5][N + 5]; // pre[pos][num] suff[len][num] int main () { cin >> t; inc(p, 1, t) { cin >> n >> m; pre[0][0] = 1; inc(i, 1, n) { inc(j, 1, i) { pre[i][j] = (pre[i - 1][j] * j + pre[i - 1][j - 1]) % m; } } inc(i, 1, n) suff[1][i] = 1; inc(i, 2, n) { inc(j, 1, n + 1 - i) { suff[i][j] = (suff[i - 1][j] * j + suff[i - 1][j + 1]) % m; } } vector<ll> res(n + 1); inc(i, 1, n) { // num inc(j, i, n) { // pos ll tmp = suff[n - j + 1][i]; if(j < n) tmp = (tmp + 3 * (n - j) * suff[n - j][i]) % m; if(j < n - 1) tmp = (tmp + (n - j) * (n - j - 1) % m * suff[n - j - 1][i]) % m; res[i] = (res[i] + pre[j - 1][i - 1] * tmp) % m; } } cout << "Case #" << p << ":\n"; inc(i, 1, n) { cout<< res[i] << " \n"[i == n]; } } }