【题解】[SCOI2008] 着色方案
[SCOI2008] 着色方案
-
题目来源:山东省选 2008,CCF;
-
在线评测地址:Luogu#2476,BZOJ#1079;
-
运行限制:\(1.00\ \text{ms}/128\ \text{MiB}\)。
题目描述
有 \(n\) 个木块排成一行,从左到右依次编号为 \(1\) 到 \(n\)。你有 \(k\) 种颜色的油漆,其中第 \(i\) 种颜色的油漆足够涂 \(c_i\) 个木块。所有油漆刚好足够涂满所有木块,即 \(\sum c_i=n\)。
由于相邻两个木块涂相同色显得很难看,所以你希望统计任意两个相邻木块颜色不同的着色方案。
输入格式
第一行为一个正整数 \(k\),第二行包含 \(k\) 个整数 \(c_1, c_2, ... , c_k\)。
输出格式
输出一个整数,即方案总数模 \(10^9 + 7\) 的结果。
数据规模与约定
- 对于 \(50\%\) 的数据,\(k\le 5\),\(c_i\le 3\);
- 对于 \(100\%\) 的数据,\(1\le k\le 15\),\(1\le c_i\le 5\)。
分析
给定 \(c_i\),\(n=\sum c_i\),要求长为 \(n\) 的数列中,数 \(i\) 出现 \(c_i\) 次,且相邻两数不同,求满足条件的数列数 \(\bmod{10^9 + 7}\)。
\(50\ \texttt{pts}\)
考虑爆搜,每一次枚举下一个字符,卡常可 \(40\sim 50\)。
\(50\ \texttt{pts}\) 提交:https://www.luogu.com.cn/record/51716210。
\(100\ \texttt{pts}\)
考虑记忆化。
注意到每种数出现的次数很少,而数的种类数较多,且数的种类并非本质不同,考虑如下状态:
\(\large{f_{a,b,c,d,e,last}}\):当有 \(a\) 种数剩下 \(1\) 个,\(b\) 种数剩下 \(2\) 个,……,\(e\) 种数剩下 \(5\) 个,且上一个数在加入前有 \(last\) 个时的方案数。
为方便讲述,这里用 \(S_i\) 表示剩下 \(i\) 个数的数构成的集合。易得 \(|S_1|=a\),\(|S_2|=b\),……,\(|S_5|=e\)。
显然,在转移时,有一种数要从 \(S_i\) 移到 \(S_{i-1}\)。我们可以枚举 \(i\),而共有 \(|S_i|\) 种数供选择。现在的问题是,如何保证前后不重复?
其实也很简单。假设数 \(v\) 从 \(S_{last}\) 转移到了 \(S_{last-1}\),那么在枚举 S_{last-1} 的时候,就需要将集合大小 \(-1\),因为有且仅有其中的 \(v\) 不满足条件。
综上,转移方程为:
初始 \(f_{0,0,0,0,0,i}=1\),最后求 \(f_{a,b,c,d,e,0}\)。
在转移时,进行了 \(5\) 次访问,复杂度为 \(\mathcal{O}(5\sum\limits_{i=1}^{k} \binom{n+4}{4})\sim \Theta(\text{能过})\)。
Code
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
constexpr int max_k = 15 + 1, mod = int(1e9) + 7;
ll dp[max_k][max_k][max_k][max_k][max_k][5]; // 记忆化
ll dfs(int a, int b, int c, int d, int e, int lst) // 搜索
{
if (dp[a][b][c][d][e][lst] != -1) // 如果有就直接返回了
return dp[a][b][c][d][e][lst];
int ret = 0;
if (a) ret = (ret + (a - (lst==1)) * dfs(a-1, b, c, d, e, 0))% mod; // 分别转移
if (b) ret = (ret + (b - (lst==2)) * dfs(a+1, b-1, c, d, e, 1))% mod;
if (c) ret = (ret + (c - (lst==3)) * dfs(a, b+1, c-1, d, e, 2))% mod;
if (d) ret = (ret + (d - (lst==4)) * dfs(a, b, c+1, d-1, e, 3))% mod;
if (e) ret = (ret + e * dfs(a, b, c, d+1, e-1, 4))% mod; // 由于没有 last=5 的情况就直接不写了
return dp[a][b][c][d][e][lst] = ret; // 赋值并返回
}
int main()
{
memset(dp, -1, sizeof dp); // 初始化
ios_base::sync_with_stdio(false);
cin.tie(0);
int n, c[5] = {};
cin >> n;
for (int i = 0, ta; i < n; i++) // 统计个数
{
cin >> ta;
c[ta-1]++;
}
for (int i = 0; i < 5; i++) // 初始化
dp[0][0][0][0][0][i] = 1;
cout << dfs(c[0], c[1], c[2], c[3], c[4], 0) << endl; // 计算、输出
return 0; // 然后就 AC 了、
}
本文来自博客园,作者 5ab,转载请注明链接哦 qwq
博客迁移啦,来看看新博客吧 -> https://5ab-juruo.oier.space/

浙公网安备 33010602011771号