传送门
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。
f[i][j][k][l][o][p]表示能有i种能用5次、j种能用4次、k种能用3次、l种能用2次、o种能用1次的油漆,最后用的一种能用p次的方案数。
由于代表的颜色不确定,所以在加的时候要乘以代表颜色的个数。同样,当用的这种油漆和之前一次的油漆能用的次数相同的时候,为了防止重复,要将其减掉。
时间效率好像有点慢……
CODE:
#include<cstdio>
const int M=1e9+7;
int num[6];
int f[16][16][16][16][16][6];
int m,t;
inline void inc(int &x,int y)
{
x=(1ll*x+y)%M;
}
inline void dec(int &x,int &y)
{
x=(1ll*x-y+M)%M;
}
inline int mul(int &x,int &y)
{
int t=1ll*x*y%M;
return t;
}
int main()
{
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%d",&t),num[t]++;
for(int i=0;i<=5;i++)
f[0][0][0][0][0][i]=1;
for(int i=0;i<=num[5];i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=m;k++)
for(int l=0;l<=m;l++)
for(int o=0;o<=m;o++)
for(int p=0;p<=4;p++)
{
if(i) inc(f[i][j][k][l][o][p],mul(f[i-1][j+1][k][l][o][4],i));
if(j)
{
inc(f[i][j][k][l][o][p],mul(f[i][j-1][k+1][l][o][3],j));
if(p==4) dec(f[i][j][k][l][o][p],f[i][j-1][k+1][l][o][3]);
}
if(k)
{
inc(f[i][j][k][l][o][p],mul(f[i][j][k-1][l+1][o][2],k));
if(p==3) dec(f[i][j][k][l][o][p],f[i][j][k-1][l+1][o][2]);
}
if(l)
{
inc(f[i][j][k][l][o][p],mul(f[i][j][k][l-1][o+1][1],l));
if(p==2) dec(f[i][j][k][l][o][p],f[i][j][k][l-1][o+1][1]);
}
if(o)
{
inc(f[i][j][k][l][o][p],mul(f[i][j][k][l][o-1][0],o));
if(p==1) dec(f[i][j][k][l][o][p],f[i][j][k][l][o-1][0]);
}
}
printf("%d",f[num[5]][num[4]][num[3]][num[2]][num[1]][0]);
return 0;
}