【题解】[SCOI2008] 着色方案

[SCOI2008] 着色方案

  • 题目来源:山东省选 2008,CCF;

  • 在线评测地址:Luogu#2476BZOJ#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\) 不满足条件。

综上,转移方程为:

\[\begin{aligned} \Large{f_{a,b,c,d,e,last}\ }\large{=}&\ (a-[last=1])\Large{f_{a-1,b,c,d,e,0}}\\ +&\ (b-[last=2])\Large{f_{a+1,b-1,c,d,e,1}}\\ +&\ \cdots\\ +&\ (e-[last=5])\Large{f_{a,b,c,d+1,e-1,4}} \end{aligned} \]

初始 \(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 了、
}
posted @ 2021-06-12 17:00  5ab  阅读(78)  评论(0编辑  收藏  举报