cf1067 A. Array Without Local Maximums(計數dp)
阿新 • • 發佈:2021-12-22
題意:
對陣列中的任何一個數,要求存在一個相鄰數大於等於它。陣列中的一些數未確定(記為-1),求填數方案數,對 998244353 取模。
n <= 2e5, 1 <= a[i] <= 200
思路:
字首和優化的dp
\(f(i,j,1)\) 表示填完了 \(a[1,i]\) ,最後一位 \(a_i=j\) ,且 \(a_{i-1} \ge a_i\) ; \(f(i,j,0)\) 表示 \(a_{i-1} < a_i\) 。
得到一個 \(O(na^2)\) 做法,會超時:
for(int i = 1; i <= n; i++) for(int j = 1; j <= 200; j++) //列舉第i位 { if(a[i] > 0 && a[i] != j) continue; for(int k = 1; k <= 200; k++) //上一位即第i-1位 { if(a[i-1] > 0 && a[i-1] != k) continue; if(j < k) f[i][j][1] += f[i-1][k][1]; if(j == k) f[i][j][1] += f[i-1][k][1] + f[i-1][k][0]; if(j > k) f[i][j][0] += f[i-1][k][1] + f[i-1][k][0]; } }
觀察發現,\(f(i,j,1)=f(i-1,j,0) + \sum_{k\ge j} f(i-1,k,1)\) ,\(f(i,j,1)= \sum_{k< j} f(i-1,k,1)+\sum_{k< j} f(i-1,k,0)\) 。用字首和優化到 \(O(na)\)。
注意第 \(i-1\) 位已經有數 \(a_{i-1}\) 說明第 \(i-1\) 層只在 \(a_{i-1}\) 處有值,其他全是0。笑死,根本不用管。
#include <bits/stdc++.h> using namespace std; using ll = long long; const int N = 1e5 + 5, p = 998244353; int n, a[N]; ll f[N][203][2]; signed main() { scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); if(a[1] > 0) f[1][a[1]][0] = 1; //初始化, 不合法的方案置零 else for(int i = 1; i <= 200; i++) f[1][i][0] = 1; for(int i = 2; i <= n; i++) { for(int j = 1; j <= 200; j++) //上一層的字首和 f[i-1][j][1] += f[i-1][j-1][1], f[i-1][j][0] += f[i-1][j-1][0]; for(int j = 1; j <= 200; j++) //第i位 { if(a[i] > 0 && a[i] != j) continue; f[i][j][1] += f[i-1][j][0] - f[i-1][j-1][0]; f[i][j][1] += f[i-1][200][1] - f[i-1][j-1][1]; f[i][j][0] += f[i-1][j-1][0] - f[i-1][0][0]; f[i][j][0] += f[i-1][j-1][1] - f[i-1][0][1]; f[i][j][1] %= p, f[i][j][0] %= p; } } ll ans = 0; for(int j = 1; j <= 200; j++) ans += f[n][j][1]; cout << (ans + p) % p; return 0; }