• [SCOI2008] 着色方案


    [SCOI2008] 着色方案(待续)

    题目大意:一排(n)个木块,(k)种油漆,每种油漆有(c_i)桶,要求相邻两个木块的颜色不能相同,求着色方案(pmod{1e9+7})

    Solution.1

    • 状态:设(f[i][j])为前(i)块被涂色的方案,且最后一块是(j)颜色
    • 转移:$f[i][j] = sum_{h=1,h e j } ^ {k}f[i-1][h] $

    好的,记录不了每桶用了多少,挂掉。

    诶,这个(c[i])有点小啊,而且如果两种油漆剩下相同的桶数,说明

    • 状态:设(f[a][b][c][d][e][last])为还剩(1)桶的有(a)种,还剩(2)桶的有(b)(dots),还剩(5)桶的有(e)种,最后一个是上一次还剩(k​)桶的油漆的方案数。
    • 转移:详见代码

    Code.1

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    
    typedef long long ll;
    
    const int Mod = 1e9 + 7;
    
    int x[6];
    ll f[16][16][16][16][16][6];
    
    ll dp(int a, int b, int c, int d, int e, int k){
    	if(a + b + c + d + e == 0) return 1;//都用光了
    	if(f[a][b][c][d][e][k]) return f[a][b][c][d][e][k] % Mod;//记搜
    	ll t = 0;
    	if(a)
    		t = (t + 1LL * (a - (k == 2)) * dp(a - 1, b, c, d, e, 1)) % Mod;
    	if(b)
    		t = (t + 1LL * (b - (k == 3)) * dp(a + 1, b - 1, c, d, e, 2)) % Mod;
    	if(c)
    		t = (t + 1LL * (c - (k == 4)) * dp(a, b + 1, c - 1, d, e, 3)) % Mod;
    	if(d)
    		t = (t + 1LL * (d - (k == 5)) * dp(a, b, c + 1, d - 1, e, 4)) % Mod;
    	if(e)
    		t = (t + 1LL * e * dp(a, b, c, d + 1, e - 1, 5)) % Mod;
    	f[a][b][c][d][e][k] = t % Mod;//假设上一次用的是可以涂k个格子的油漆,那么上一次用完后,那种油漆就只可以涂k - 1个格子了,那么这一次涂油漆的时候就不能再选可以涂k - 1个格子的油漆的一种,因为这种上次已经用过了,且相同的油漆不能相邻。
    	return t % Mod;
    }
    
    int main(){
    	int k;
    	scanf("%d", &k);
    	for(int i = 1, la; i <= k; ++i){
    		scanf("%d", &la);
    		x[la] ++;
    	}
    	printf("%lld
    ", dp(x[1], x[2], x[3], x[4], x[5], 0) % Mod);
    	return 0;
    }
    

    Solution.2

    排列组合搞(DP)

    • 状态:(f[i][j])表示使用了前(i)种油漆,有(j)个相邻的同色快的方案数

    考虑新加一种油漆(i+1),我们假设有(x)个,我们先将他们分组。

    设分成了(q)组,用的插板和插空思想,有(q-1)个板子,有(x-1)个空,那么方案数就是(C_{x-1}^{q-1}).

    并会产生(x-q)个相邻的色块,因为如果不分组,那么会产生(x-1)个相邻色块,分成(q)组,会减少(q-1)个相邻色块(手动画一画),所以最终产生的相邻色块是((x-1)-(q-1)=x-q)

    下一步我们要把分好的组插入原来的木块中间(包括两边),所以总共就有(sum[i]+1)个空,其中有(j)个空两边颜色相同,我们用这(q)组中的(b)个插入左右颜色相同的位置,(C_{j}^{b})就是插入到之前同色的之间的方案数(位置不同),(C_{sum[i]+1−j}^{q−b})就是相当于前面有(sum[i]+1)个位置,(j)个相邻块占据的位置不能放,(q-b)插入进去的方案数

    最后剩下的相邻色块就是(j-b+(x-q)​)

    [f[i + 1][j - b + (x - q)] = C_{x-1}^{q-1}cdot C_{sum[i]+1−j}^{q−b} cdot C_j^bcdot f[i][j] ]

    Code.2

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 106;
    const int P = 1e9 + 7;
    
    int k;
    int a[N], sum[N];
    int c[N][N];
    int f[N][N];
    
    inline void pre_work() {
    	c[0][0] = 1;
    	for(int i = 1; i <= 75; ++i){
    		c[i][0] = 1;
    		for(int j = 1; j <= i; ++j) 
    			c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % P;
    	}
    	return;
    }
    
    int main() {
    	scanf("%d", &k);
    	for(int i = 1; i <= k; ++i) {
    		scanf("%d", &a[i]);
    		sum[i] = sum[i - 1] + a[i];
    	}
    	pre_work();
    	f[1][a[1] - 1] = 1;//a[1] - 1 是最多对数 
    	for(int i = 1; i < k; ++i)
    		for(int j = 0; j < sum[i]; ++j) {
    			if(!f[i][j]) continue;
    			for(int qwq = 1; qwq <= a[i + 1]; ++qwq)//最多能分成多少组
    				for(int b = 0; b <= qwq && b <= j; ++b){//从qwq里面调不大于j的b个插到之前色块相邻的地方
    					int res = 1LL * f[i][j] * c[a[i + 1] - 1][qwq - 1] % P * c[j][b] % P;
    					res = 1LL * c[sum[i] + 1 - j][qwq - b] % P * res % P;//剩下的
    					f[i + 1][j + a[i + 1] - qwq - b] = (f[i + 1][j + a[i + 1] - qwq - b] + res) % P;
    				} 
    		} 
    	printf("%d
    ", f[k][0]);
    	return 0;
    }
    

    2018-09-24 14:39

  • 相关阅读:
    yum管理工具
    ansible简介,简单实用
    ssh服务简介及应用与服务的进程的类型
    keepliave
    NFS
    编译安装redis4.0
    redis多实例和高可用
    NTP时间服务
    ssl简介与openssl的使用
    一些高效学习方法-牛人是怎么来的
  • 原文地址:https://www.cnblogs.com/LMSH7/p/9564768.html
Copyright © 2020-2023  润新知