• 【题解】[SCOI2008] 着色方案


    [SCOI2008] 着色方案

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

    • 在线评测地址:Luogu#2476BZOJ#1079

    • 运行限制:(1.00 ext{ms}/128 ext{MiB})

    题目描述

    (n) 个木块排成一行,从左到右依次编号为 (1)(n)。你有 (k) 种颜色的油漆,其中第 (i) 种颜色的油漆足够涂 (c_i) 个木块。所有油漆刚好足够涂满所有木块,即 (sum c_i=n)

    由于相邻两个木块涂相同色显得很难看,所以你希望统计任意两个相邻木块颜色不同的着色方案。

    输入格式

    第一行为一个正整数 (k),第二行包含 (k) 个整数 (c_1, c_2, ... , c_k)

    输出格式

    输出一个整数,即方案总数模 (10^9 + 7) 的结果。

    数据规模与约定

    • 对于 (50\%) 的数据,(kle 5)(c_ile 3)
    • 对于 (100\%) 的数据,(1le kle 15)(1le c_ile 5)

    分析

    给定 (c_i)(n=sum c_i),要求长为 (n) 的数列中,数 (i) 出现 (c_i) 次,且相邻两数不同,求满足条件的数列数 (mod{10^9 + 7})

    (50 exttt{pts})

    考虑爆搜,每一次枚举下一个字符,卡常可 (40sim 50)

    (50 exttt{pts}) 提交:https://www.luogu.com.cn/record/51716210

    (100 exttt{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) 不满足条件。

    综上,转移方程为:

    [egin{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}(5sumlimits_{i=1}^{k} inom{n+4}{4})sim Theta( ext{能过}))

    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 会尽力达到。

    如果您真的不喜欢这篇文章,也请不要喷,5ab 欢迎私信反馈意见。

  • 相关阅读:
    【Django】DRF序列化器之Serializers家族
    【Django】DRF源码分析之五大模块
    【Django】DRF源码分析对比原生Django
    【MySQL】主从复制
    【MySQL】锁之InnoDB
    【MySQL】锁之MyISAM
    【安装】Ubuntu之Redis
    Git安装配置以及常用命令整理
    10、Python 数据分析-Matplotlib绘图大全详解
    8、Python 数据分析-Pandas高级操作
  • 原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-scoi2008_zhuo_se_fang_an.html
Copyright © 2020-2023  润新知