因为之前做过水淹笛卡尔树的题(ARC117E),一眼看出了 DP
状态,后面就不会了,题解告诉我第二维有效状态不超过
个,人有点麻。也是没有仔细思考的缘故。
按值域从小到大 DP,每次考虑插入 个 。
假设已经插入的数形成了
个连续段,显然,除了最左和最右的连续段,剩下的段的两端都是 (不然就无法满足相邻位置差为 )
这提示我们最左边和最右边有一些特殊性。
事实上,如果最左边是 ,那么当前的
还可以插到左边,否则不行。右边同理。
那么我们设
表示当前做到 ,一共形成了 个连续段,最左边是不是 ,最右边是不是 。
转移有两个方面,第一是
的变化,第二是转移系数。
如果我们想在最左和最右都填上
的话,我们首先会把 个 填在每个连续段的两侧,然后剩余的 个 会贡献 个连续段,总段数就是 。
如果想在某几侧不填上
的话,道理是一样的,将结果 或
即可。
考察转移系数,还是以
为例,这时一共会有
个空隙,每个空隙都必须有数,这就是个插板法,方案数为 。
直接这么做,看上去可以达到平方复杂度,但是,我们发现
的变化方法较为单一,它只有可能在每轮中由 变为 ,或在 或 变化时加上 或者 。
我们处理出 表示如果前
轮,每轮都在两边放数,会有几个连续段(显然有 )。
那么 的取值只会在 附近,取 即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include <bits/stdc++.h> using namespace std; const int N = 2e5 + 5,P = 998244353; inline void Plus(int &x,const int &y) { x += y;if(x >= P) x -= P;} int n,a[N],cur[N]; int dp[N][7][2][2]; int fac[N],ifac[N]; inline void init(int n) { fac[0] = 1; for(int i = 1;i <= n;i++) fac[i] = 1ll * fac[i - 1] * i % P; ifac[1] = 1; for(int i = 2;i <= n;i++) ifac[i] = 1ll * ifac[P % i] * (P - P / i) % P; ifac[0] = 1; for(int i = 1;i <= n;i++) ifac[i] = 1ll * ifac[i - 1] * ifac[i] % P; } inline int C(int n,int m) { if(n < 0 || m < 0 || n < m) return 0;return 1ll * fac[n] * ifac[m] % P * ifac[n - m] % P;} int main() { cin >> n; for(int i = 1;i <= n;i++) cin >> a[i]; cur[1] = a[1]; dp[1][2][1][1] = 1; init(2e5);
for(int i = 2;i <= n;i++) { cur[i] = a[i] - cur[i - 1]; for(int j = 0;j <= 6;j++) for(int u = 0;u <= 1;u++) for(int v = 0;v <= 1;v++) if(dp[i - 1][j][u][v]) { for(int p = 0;p <= u;p++) for(int q = 0;q <= v;q++) { int val = dp[i - 1][j][u][v],c = cur[i - 1] + j - 2; if(p == 0 && q == 0) Plus(dp[i][6 - j][p][q],1ll * val * C(a[i] - 1,c - 2) % P); if(p == 1 && q == 1 && j <= 4) Plus(dp[i][4 - j][p][q],1ll * val * C(a[i] - 1,c) % P); if(p + q == 1 && j <= 5) Plus(dp[i][5 - j][p][q],1ll * val * C(a[i] - 1,c - 1) % P); } } for(int j = 0;j <= 6;j++) for(int u = 0;u <= 1;u++) for(int v = 0;v <= 1;v++) printf("f[%d][%d][%d][%d]=%d\n",i,cur[i] + j - 2,u,v,dp[i][j][u][v]); } int ans = 0; for(int i = 0;i <= 6;i++) for(int u = 0;u <= 1;u++) for(int v = 0;v <= 1;v++) if(cur[n] + i - 2 == 1) Plus(ans,dp[n][i][u][v]); cout << ans << endl; return 0; }
|