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
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 }