← 全部文章

ARC100F Colorful Sequences 题解

本文目录(3 节)
  1. 题意
  2. 题解
  3. 代码

ARC100F Colorful Sequences

题意

  • 给定 n,kn,k 和序列 a1…ma_{1\dots m}。
  • 定义存在「长度为 kk 的连续子序列是一个连续段」的序列为 colorful 序列。
  • 求所有长度为 nn 值域 [1,k][1,k] 的 colorful 序列的所有连续子序列中有多少个恰好是 a1…ma_{1 \dots m}。
  • n≤2.5×104n \le 2.5 \times 10^4,k≤400k \le 400,答案对 109+710^9+7 取模。

题解

答案等于所有序列的答案减去不 colorful 序列的答案。

前者答案为 kn−m(n−m+1)k^{n-m}(n-m+1),考虑如何计算后者,即计算不 colorful 序列中 a1…ma_{1\dots m} 的个数。

先考虑一个这样一个问题:计算不 colorful 序列的个数。

考虑 DP,设 fi,jf_{i,j} 表示考虑前 ii 个数,当前极长的没有重复数的后缀长度为 jj 的方案数。

初始状态为 f0,0=1f_{0,0} = 1,目标答案为 ∑j=0k−1fn,j\sum_{j=0}^{k-1} f_{n,j},转移如下: fi,j←fi−1,j′⋅{0j′+1<jk−j′j′+1=j1j′+1>j f_{i,j} \leftarrow f_{i-1,j^\prime} \cdot\begin{cases}0 & j^\prime + 1 < j \\\\k - j^\prime & j^\prime + 1 = j \\\\1 & j^\prime + 1 > j\end{cases} 复杂度 O(nk2)\mathcal O(nk^2),前缀和优化一下为 O(nk)\mathcal O(nk)。

接下来考虑原问题:计算不 colorful 序列中 a1…ma_{1\dots m} 的个数。

考虑三种情况:

  1. a1…ma_{1\dots m} 本身就是 colorful 序列,答案显然为 00。
  2. 否则,若 a1…ma_{1\dots m} 中的数互不相同,但由于 m<km<k 所以不是 colorful 序列。
    考虑计算不 colorful 序列中「长度为 mm 的其中的数互不相同的连续子序列」个数,然后除以 km‾k^{\underline{m}} 就是答案。
    计算方法就是上面的 DP,另外定义一个 gi,jg_{i,j} 表示 fi,jf_{i,j} 中 a1…ma_{1\dots m} 的个数,当 j≥mj \ge m 时让 gi,jg_{i,j} 加上 fi,jf_{i,j},然后一起转移即可。
  3. 否则,意味着 a1…ma_{1 \dots m} 中有相同的数且不是 colorful 序列。
    先求出极长的没有重复数的前后缀长度,设为 l,rl,r。
    同样采用上面的 DP,只是将初值改成 f0,l=1f_{0,l} = 1,同理设 g0,r=1g_{0,r} = 1。
    枚举 a1…ma_{1\dots m} 的开头位置 ii,则方案数为 (∑j=0k−1fi−1,j)(∑j=0k−1gn−(i−1)−m,j)\left(\sum_{j=0}^{k-1} f_{i-1,j}\right)\left(\sum_{j=0}^{k-1} g_{n-(i-1)-m,j}\right)。

总时间复杂度 O(nk)\mathcal O(nk)。

代码

const int N = 2.5e4 + 7, K = 407;
int n, k, m, a[N], l, r;
bool v[K];
modint f[N][K], g[N][K], ans;

inline void clear() {
	for (int i = 1; i <= k; i++) v[i] = 0;
}

inline bool pd(int l, int r) {
	clear();
	for (int i = l; i <= r; i++)
		if (v[a[i]]) return 0;
		else v[a[i]] = 1;
	return 1;
}

inline bool colorful() {
	for (int l = 1, r = k; r <= m; l++, r++)
		if (pd(l, r)) return 1;
	return 0;
}

inline void DP(modint f[N][K]) {
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j < k; j++)
			f[i][j] = (f[i-1][j-1] - f[i-1][j]) * (k - j + 1) + f[i-1][j];
		for (int j = k - 1; ~j; j--) f[i][j] += f[i][j+1];
	}
}

int main() {
	rd(n, k, m), rda(a, m);
	if (colorful()) return print(((modint)k ^ (n - m)) * (n - m + 1)), 0;
	clear();
	for (int i = 1; i <= m; i++)
		if (v[a[i]]) break;
		else v[a[i]] = 1, ++l;
	clear();
	for (int i = m; i; i--)
		if (v[a[i]]) break;
		else v[a[i]] = 1, ++r;
	if (l == m) {
		f[0][0] = 1;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j < k; j++)
				f[i][j] = (f[i-1][j-1] - f[i-1][j]) * (k - j + 1) + f[i-1][j],
				g[i][j] = (g[i-1][j-1] - g[i-1][j]) * (k - j + 1) + g[i-1][j] + (j >= m) * f[i][j];
			for (int j = k - 1; ~j; j--)
				f[i][j] += f[i][j+1], g[i][j] += g[i][j+1];
		}
		ans = g[n][0];
		for (int i = k; i > k - m; i--) ans /= i;
	} else {
		for (int i = 0; i <= l; i++) f[0][i] = 1;
		for (int i = 0; i <= r; i++) g[0][i] = 1;
		DP(f), DP(g);
		for (int i = 1, j = m; j <= n; i++, j++)
			ans += f[i-1][0] * g[n-j][0];
	}
	print((((modint)k ^ (n - m)) * (n - m + 1)) - ans);
	return 0;
}

标签:DP、容斥、组合计数

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…