1079: [SCOI2008]着色方案
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2826 Solved: 1682
[Submit][Status][Discuss]
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,也想到用dp[a][b][c][d][e]维护数量为1,2,3,4,5,的颜色有a,b,c,d,e种的方案数,但是不太会递推啊...
题解:
看黄学长的题解用的记忆化搜索,用last记录上一层用掉的是数量为last的颜色,那么该层能用的last-1的颜色就少了一个,用f[a][b][c][d][e][last]维护数量为1,2,3,4,5,的颜色有a,b,c,d,e种的方案数。
当用掉一个数量为k的颜色时,增加的方案数为该层能用的数量为k的颜色数(即 num[k] -(last == k+1))*dfs(..,num[k-1]+1,num[k]-1,...,k)。
1 /************************************************************** 2 Problem: 1079 3 User: mizersy 4 Language: C++ 5 Result: Accepted 6 Time:88 ms 7 Memory:151292 kb 8 ****************************************************************/ 9 10 #include <bits/stdc++.h> 11 using namespace std; 12 typedef long long ll; 13 const ll mod = 1e9+7; 14 ll f[20][20][20][20][20][6]; 15 ll k,w; 16 ll num[10]; 17 18 ll dfs(ll a,ll b,ll c,ll d,ll e,ll last){ 19 if (f[a][b][c][d][e][last]) return f[a][b][c][d][e][last]; 20 ll ret = 0; 21 if (a) 22 ret = (ret + (a - (last == 2)) * dfs(a-1,b,c,d,e,1) % mod) % mod; 23 if (b) 24 ret = (ret + (b - (last == 3)) * dfs(a+1,b-1,c,d,e,2) % mod) % mod; 25 if (c) 26 ret = (ret + (c - (last == 4)) * dfs(a,b+1,c-1,d,e,3) % mod) % mod; 27 if (d) 28 ret = (ret + (d - (last == 5)) * dfs(a,b,c+1,d-1,e,4) % mod) % mod; 29 if (e) 30 ret = (ret + e * dfs(a,b,c,d+1,e-1,5) % mod) % mod; 31 return f[a][b][c][d][e][last] = ret % mod; 32 } 33 34 35 int main(){ 36 scanf("%lld",&k); 37 for (int i = 1;i <= k;++i){ 38 scanf("%lld",&w); 39 num[w]++; 40 } 41 for (int i = 0;i <= 5;++i) f[0][0][0][0][0][i] = 1; 42 printf("%lld ",dfs(num[1],num[2],num[3],num[4],num[5],0)); 43 }