• Bzoj1079:[SCOI2008]着色方案


    题面

    传送门

    Sol1

    因为每种油漆的数量是有限的
    并且每种油漆是没有优先级的
    直接设状态(f[lst][a][b][c][d][e])表示有(a)个可以涂一次,(b)个可以涂两次......上次涂的是可以涂(lst)次的
    记搜+乘法原理即可

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int Zsy(1e9 + 7);
    
    IL ll Input(){
    	RG ll x = 0, z = 1; RG char c = getchar();
    	for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    	for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    	return x * z;
    }
    
    int k, c[6];
    int f[6][16][16][16][16][16];
    
    IL int Dfs(RG int lst, RG int a, RG int b, RG int c, RG int d, RG int e){
    	if(f[lst][a][b][c][d][e] != -1) return f[lst][a][b][c][d][e];
    	if(a + b + c + d + e == 0) return 1;
    	RG int ret = 0, aa = a, bb = b, cc = c, dd = d, ee = e;
    	aa -= (lst == 2); bb -= (lst == 3); cc -= (lst == 4); dd -= (lst == 5);
    	if(aa > 0) (ret += 1LL * aa * Dfs(1, a - 1, b, c, d, e) % Zsy) %= Zsy;
    	if(bb > 0) (ret += 1LL * bb * Dfs(2, a + 1, b - 1, c, d, e) % Zsy) %= Zsy;
    	if(cc > 0) (ret += 1LL * cc * Dfs(3, a, b + 1, c - 1, d, e) % Zsy) %= Zsy;
    	if(dd > 0) (ret += 1LL * dd * Dfs(4, a, b, c + 1, d - 1, e) % Zsy) %= Zsy;
    	if(ee > 0) (ret += 1LL * ee * Dfs(5, a, b, c, d + 1, e - 1) % Zsy) %= Zsy;
    	return f[lst][a][b][c][d][e] = ret % Zsy;
    }
    
    int main(RG int argc, RG char* argv[]){
    	Fill(f, -1);
    	k = Input();
    	for(RG int i = 1, t; i <= k; ++i) ++c[t = Input()];
    	printf("%d
    ", Dfs(0, c[1], c[2], c[3], c[4], c[5]) % Zsy);
    	return 0;
    }
    
    

    Sol2

    还有更优秀的组合数学+(DP)的做法
    (sum[i])表示(c[i])的前缀和,(C[i][j])(C_{i}^{j}),大小写区分开
    (f[i][j])表示用了前(i)种颜色涂了(sum[i])个块,其中有(j)对相邻同色块的方案数
    考虑转移(f[i][j])
    (c[i + 1])分成(a)组的插入到已经弄好的块中
    (b)组插入到之前同色的之间
    (a-b)组插空放不相邻
    那么就是转移给(f[i + 1][j - b + c[i + 1] - a])
    方案数为(f[i][j] * C[c[i + 1] - 1][a - 1] * C[j][b] * C[sum[i] + 1 - j][a - b])

    (C[c[i + 1] - 1][a - 1])就是分成(a)组的方案数(无序的)

    (C[j][b])就是插入到之前同色的之间的方案数(位置不同)

    (C[sum[i] + 1 - j][a - b])就是相当于前面有(sum[i]+1)个位置,(j)个相邻块占据的位置不能放,(a-b)插入进去的方案数

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int Zsy(1e9 + 7);
    
    IL ll Input(){
    	RG ll x = 0, z = 1; RG char c = getchar();
    	for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    	for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    	return x * z;
    }
    
    int k, c[16], C[80][80], f[20][80], sum[16];
    
    IL void Prepare(){
    	C[0][0] = 1;
    	for(RG int i = 1; i <= 75; ++i){
    		C[i][0] = 1;
    		for(RG int j = 1; j <= 75; ++j)
    			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % Zsy;
    	}
    }
    
    int main(RG int argc, RG char* argv[]){
    	Prepare();
    	k = Input();
    	for(RG int i = 1; i <= k; ++i) c[i] = Input(), sum[i] = sum[i - 1] + c[i];
    	f[1][c[1] - 1] = 1;
    	for(RG int i = 1; i < k; ++i)
    		for(RG int j = 0; j < sum[i]; ++j){
    			if(!f[i][j]) continue;
    			for(RG int a = 1; a <= c[i + 1]; ++a)
    				for(RG int b = 0; b <= a && b <= j; ++b){
    					RG int ret = 1LL * f[i][j] * C[c[i + 1] - 1][a - 1] % Zsy * C[j][b] % Zsy;
    					ret = 1LL * ret * C[sum[i] + 1 - j][a - b] % Zsy;
    					(f[i + 1][j + c[i + 1] - a - b] += ret) %= Zsy;
    				}
    		}
    	printf("%d
    ", f[k][0]);
    	return 0;
    }
    
    
  • 相关阅读:
    关于MySQL中的TRUNCATE语句
    关于在如何linux上部署禅道
    关于Python中的for...else...语句格式
    关于python中身份标识"is"与值运算符"=="
    Vite Vue3.0 使用 SVG Icon (自定义Vite插件)
    Python 远程开发树莓派 点亮LED灯
    Vue 基于elementUI的电梯导航
    JavaScript 原生数字千分位格式化函数(多功能的toLocaleString)
    JavaScript IntersectionObserver 图片懒加载及文字动态划线
    JavaScript await 优雅的捕获异常
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/8358062.html
Copyright © 2020-2023  润新知