ARC146E 题解

First Post:

Last Update:

因为之前做过水淹笛卡尔树的题(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]; // 如果两边都不放 i,会有多少对相邻的 i
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; // c 表示实际有多少对相邻的 i
if(p == 0 && q == 0)
Plus(dp[i][6 - j][p][q],1ll * val * C(a[i] - 1,c - 2) % P); // 4 - j : a - 原段数 + 2
if(p == 1 && q == 1 && j <= 4)
Plus(dp[i][4 - j][p][q],1ll * val * C(a[i] - 1,c) % P); // 2 - j: a - 原段数
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;
}