视频加载失败

安吉-开营测试 T4

931 字
5 分钟
安吉-开营测试 T4
原题呈现

题目描述#

香风智乃想画一幅画. 这幅画一共有 nn 个像素,每个像素上可以画 mm 种颜色的其中一种. 但是,如果某一种颜色连续重复出现太多次,那么就会显得这幅画很单调.具体来说,智乃不希望第 ii 种颜色连续出现超过 aia_i 次. 智乃想知道,她一共能画出多少种不同的画.两幅画不同,当且仅当某个像素上所画的颜色不同.

输入格式#

从文件 draw.in 中读入数据. 第一行输入两个整数 n,mn, m. 第二行输入 mm 个整数,第 ii 个表示 aia_i

输出格式#

输出到文件 draw.out 中. 输出一个非负整数,表示答案对 998244353 取模后的值.

样例输入#

3 3
1 2 3

样例输出#

21

数据范围#

对于 10%10\% 的数据,1n,m51 \leq n, m \leq 5. 对于 30%30\% 的数据,1n,m501 \leq n, m \leq 50. 对于 50%50\% 的数据,1n,m5001 \leq n, m \leq 500. 对于 70%70\% 的数据,1n,m50001 \leq n, m \leq 5000. 对于 100%100\% 的数据,1ain5000,1m1051 \leq a_i \leq n \leq 5000, 1 \leq m \leq 10^5

观察 nn 的范围,显然使用 dp.

定义状态:dp[i][j] 表示考虑前 i 个像素,最后一个颜色为 j 的方案数.

一般情况下,对于一个序列,该序列的下一个像素都可以取 mm 种颜色,但是有一个例外.由于不能连续重复出现太多次这一限制的存在,存在一种危险序列,满足以下所有条件:

  • 该序列的后面 aka_k 个都是颜色 kk
  • 该序列的第倒数 ak+1a_k+1 个像素的颜色不是颜色 kk

此时,这个序列的下一个像素就不能是颜色 kk

若定义前 ii 个像素的合法方案数为 sis_i(无最后一个颜色的限制),合法但是后面不能再增加颜色 jj 的方案数为 di,jd_{i,j},可以预处理出 sis_i,通过容斥,先计算出无视这一限制后的方案数 si1s_{i - 1}(直接往所有合法序列后面添加颜色 jj),再减去上述的不合法方案数 di1d_{i - 1},就是 dp[i][j]

si=j=1mdp[i][j]s_i = \sum_{j=1}^{m} dp[i][j] di,j=siajdp[iaj][j]d_{i,j} = s_{i-a_j} - dp[i-a_j][j]

dp[i][j] 的表达式为:

si1(siaj1dp[iaj1][j])s_{i-1} - (s_{i-a_j-1}-dp[i-a_j-1][j])

最终结果为 s[n]

这种方法的时间复杂度为 O(mn)O(mn)

观察到颜色种类很多,但是限制种类很少,说明有多种颜色的限制相同.那么这些颜色就可以一起处理.

修改状态:dp[i][j] 表示考虑前 i 个像素,最后一个颜色是限制为 j 的某一种颜色的方案数.

定义 cnt[i] 表示限制为 ii 的颜色数量.

由此,我们修改 sis_idid_i 的表达式:

si=kadp[i][k]×cnt[k]s_i = \sum_{k\in a} dp[i][k] \times cnt[k] di=sikdp[ik][k]d_i = s_{i-k} - dp[i-k][k] dp[i][k]=si1(sik1dp[ik1][k])dp[i][k] = s_{i-1} - (s_{i-k-1}-dp[i-k-1][k])

这样的时间复杂度为 O(n2+m)O(n^2+m)

标程

#include <bits/stdc++.h>
using namespace std;
const int M = 1e5 + 100;
const int N = 5005;
const int MOD = 998244353;
int n, m;
map<int, int> cnt;
int dp[N][N], s[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int a;
cin >> a;
cnt[a]++;
}
vector<int> keys;
for (auto i : cnt) {
if (i.second != 0)
keys.push_back(i.first);
}
s[0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < keys.size(); j++) {
int k = keys[j];
if (i - k <= 0) {
dp[i][k] = s[i - 1];
} else
dp[i][k] = (0ll + s[i - 1] - (s[i - k - 1] - dp[i - k - 1][k]) + MOD) % MOD;
}
for (int j = 0; j < keys.size(); j++) {
int k = keys[j];
(s[i] += 1ll * dp[i][k] * cnt[k] % MOD) %= MOD;
}
}
cout << s[n];
}

实际上,若一个 dp 状态与之前的连续的一段状态(开头为位置 pp)都有关,则可能可以从状态 p1p-1 进行转移.

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

安吉-开营测试 T4
https://blog.jerrylab.top/posts/problem/anji2026/D0/T4/
作者
Jerry
发布于
2026-08-05
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Jerry
Hello, I'm Jerry.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
最新动态
站点统计
文章
80
分类
3
标签
35
总字数
89,198
运行时长
0
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0