在空无一物的时光深处 题解

考虑倒着做。那么就是覆盖。假设我们倒着考虑到了第 \(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;
}
posted @ 2025-11-19 17:34  Zctf1088  阅读(23)  评论(0)    收藏  举报