bzoj1079[SCOI2008]着色方案

传送门

Description

  有n个木块排成一行,从左到右依次编号为1~n。你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块。
所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n。相邻两个木块涂相同色显得很难看,所以你希望统计任意两
个相邻木块颜色不同的着色方案。

Input

  第一行为一个正整数k,第二行包含k个整数c1, c2, ... , ck。

Output

  输出一个整数,即方案总数模1,000,000,007的结果。

Sample Input

3
1 2 3

Sample Output

10

HINT

 100%的数据满足:1 <= k <= 15, 1 <= ci <= 5

题解

 我们观察到直接dp显然是不行的,并且对于同样数量的不同颜色产生的效果是相同的,并且每种颜色最多涂5次。因此我们考虑将dp的状态设为当前有多少个颜色的色块的数量是相同的。即dp[a][b][c][d][e][be]表示能涂一次的颜色有a种,能涂两次的颜色有b种,以此类推,be表示上一次涂的色块本来有be块。然后转移就很显然了,只需要注意一下如果本次转移的是be-1的色块,则乘的色块数应当-1。用记忆化搜索就可以做了。

代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7 #define ll long long
 8 #define mo 1000000007
 9 using namespace std;
10 ll dp[16][16][16][16][16][6];
11 ll ans;
12 int c[6];
13 int n;
14 bool used[16][16][16][16][16][6];
15 ll dfs(int a,int b,int c,int d,int e,int be){
16     ll tot=0;
17     if(used[a][b][c][d][e][be])  return dp[a][b][c][d][e][be];
18     if(a+b+c+d+e==0)  return 1;
19     if(a)  tot=(tot+(a-(be==2))*dfs(a-1,b,c,d,e,1))%mo;
20     if(b)  tot=(tot+(b-(be==3))*dfs(a+1,b-1,c,d,e,2))%mo;
21     if(c)  tot=(tot+(c-(be==4))*dfs(a,b+1,c-1,d,e,3))%mo;
22     if(d)  tot=(tot+(d-(be==5))*dfs(a,b,c+1,d-1,e,4))%mo;
23     if(e)  tot=(tot+e*dfs(a,b,c,d+1,e-1,5))%mo;
24     used[a][b][c][d][e][be]=1;
25     dp[a][b][c][d][e][be]=tot%mo;
26     return dp[a][b][c][d][e][be];
27 }
28 int main(){
29     scanf("%d",&n);
30     int i,j,x;
31     for(i=1;i<=n;++i){
32         scanf("%d",&x);
33         c[x]++;
34     }
35     ans=dfs(c[1],c[2],c[3],c[4],c[5],0);
36     printf("%lld\n",ans);
37     return 0;
38 }

 

 

 

posted @ 2018-06-11 14:16  lazytear  阅读(112)  评论(0编辑  收藏  举报