在空无一物的时光深处 题解
考虑倒着做。那么就是覆盖。假设我们倒着考虑到了第 \(i\) 个元素。那么要使 \(i\) 露出来,那么当前所处的位置一定是区间的左端点或者右端点。
设 \(f_{i,j,0/1}\) 表示倒着考虑到了 \(i\),连续段长度为 \(j\)。那么转移其实是不难的。不难发现,\(f_{i,j,0}\) 和 \(f_{i,j,1}\) 的转移是相同的。以 \(f_{i,j,0}\) 为例,一下均为 \(f_{i,j,0}\) 的相关转移及条件:
-
\(f_{i,j,0} \rightarrow f_{k,x,0}\),即向左延伸至长度为 \(x\)。
-
\(f_{i,j,0} \rightarrow f_{k,x,1}\),即向右延伸至长度为 \(x\)。
上式满足 \(1 \le k<i\) 并且 \(j<x \le m\)
解释一下这个转移。那就是我们从 \(i-1\) 开始一直到 \(k+1\) 的这些颜色,他都是在 \([1,j]\) 的这个区间里一直走来走去,然后到了 \(k\) 这个点,他就露出来了。
考虑什么情况下 \((i,j,0)\) 到 \((k,x,0/1)\) 是合法的转移。对于每个 \((i,j)\),考虑一个 \(g_{k,x} \in \{0,1\}\),表示从 \([1,j]\) 的这个左端点(即 \(1\))出发,通过 \(i+1\) 到 \(k\) 这些颜色一直在 \([1,j]\) 内走来走去,最后能否到达 \(x\)。这个是好转移的。
然后大体上转移及限制条件就这些。
然后有一些转移上的细节问题。
拿样例来举例子:
5 6
2 3 2 2 3
我们通过暴力代码发现,5 5 5 这个状态是,从左端点和右端点出发,都可以到达 5 5 5 2 2 这个状态。这两种转移方式本质上是没有区别的,所以就会造成重复转移的问题。
所以说,我们对于 \(i=n\) 的时候,我们不能让他 \(f_{n,j,0}\) 和 \(f_{n,j,1}\) 对同一个状态作贡献。写一个 vis 判一下即可。
#include <bits/stdc++.h>
#define il inline
#define ull unsigned long long
using namespace std;
const int bufsz = 1 << 20;
char ibuf[bufsz], *p1 = ibuf, *p2 = ibuf;
#define getchar() (p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, bufsz, stdin), p1 == p2) ? EOF : *p1++)
il int read() {
int x = 0; char ch = getchar(); bool t = 0;
while (ch < '0' || ch > '9') {t ^= ch == '-'; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return t ? -x : x;
}
bool Beg;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;
const int P = 131;
const int N = 150 + 5;
int n, m, c[N];
int f[N][N][2], g[N][N];
il void add(int &x, int y) {
x = x + y >= MOD ? x + y - MOD : x + y;
}
char vis[N][N][N][2];
bool End;
il void Usd() {cerr << "\nUse: " << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n";}
signed main() {
freopen("stare.in", "r", stdin);
freopen("stare.out", "w", stdout);
n = read(), m = read();
for (int i = 1; i <= n; i++) c[i] = read();
int ans = 0;
f[n][c[n]][0] = 1, f[n][c[n]][1] = (c[n] > 1);
for (int i = n; i >= 1; i--) {
for (int j = 1; j <= m; j++) {
for (int v = 0; v < 2; v++) {
if (!f[i][j][v]) continue;
for (int k = 1; k <= i; k++)
for (int x = 1; x <= j; x++) g[k][x] = 0;
g[i][v ? j : 1] = 1;
for (int k = i - 1; k >= 1; k--) {
for (int x = 1; x <= j; x++) {
if (x - c[k] + 1 >= 1) g[k][x - c[k] + 1] |= g[k + 1][x];
if (x + c[k] - 1 <= j) g[k][x + c[k] - 1] |= g[k + 1][x];
}
for (int x = j + 1; x <= m; x++) {
int p = j - x + c[k];
if (1 <= p && p <= j && g[k + 1][p] && !(i == n && vis[j][k][x][0])) {
add(f[k][x][0], f[i][j][v]);
vis[j][k][x][0] = 1;
}
p = 1 + x - c[k];
if (1 <= p && p <= j && g[k + 1][p] && !(i == n && vis[j][k][x][1])) {
add(f[k][x][1], f[i][j][v]);
vis[j][k][x][1] = 1;
}
}
}
if (i == n && v) continue;
int fl = 0;
for (int x = 1; x <= j; x++) fl |= g[1][x];
add(ans, 1ll * fl * f[i][j][v] * (m - j + 1) % MOD);
}
}
}
printf("%d\n", ans);
Usd();
return 0;
}

浙公网安备 33010602011771号