bzoj 1079 着色方案

Written with StackEdit.

Description

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

Input

第一行为一个正整数\(k\),第二行包含\(k\)个整数\(c_1, c_2, ... , c_k\)

Output

输出一个整数,即方案总数模\(10^9+7\)的结果。

Sample Input

3
1 2 3

Sample Output

10

HINT

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

Solution

  • 看上去很像状压?然而状压是压不下的...
  • 关键在于,两种油漆,如果可使用次数相同,那么它们其实是等价的.
  • 也就是说我们实际需要考虑的只有\(5\)种.
  • 直接开\(5\)维,用\(f[a][b][c][d][e][pre]\)来表示各个次数的颜色剩余的种类以及上个颜色原来的次数确定时的方案数.
  • 转移时需要注意,若上个是种类\(i\),那么就会有一种颜色的次数减少了\(1\),导致\(i-1\)这一位传递进来时加了\(1\).但我们不能用它,在统计时要减去.
#include<bits/stdc++.h>
using namespace std;
typedef long long LoveLive;
inline int read()
{
	int out=0,fh=1;
	char jp=getchar();
	while ((jp>'9'||jp<'0')&&jp!='-')
		jp=getchar();
	if (jp=='-')
		{
			fh=-1;
			jp=getchar();
		}
	while (jp>='0'&&jp<='9')
		{
			out=out*10+jp-'0';
			jp=getchar();
		}
	return out*fh;
}
const int P=1e9+7;
inline int add(int a,int b)
{
	return (a + b) % P;
}
inline int mul(int a,int b)
{
	return 1LL * a * b % P;
}
int n=0,k;
const int MAXK=16;
int col[16],t[6];
int f[MAXK][MAXK][MAXK][MAXK][MAXK][6];
int dfs(int a,int b,int c,int d,int e,int pre)
{
	int &res=f[a][b][c][d][e][pre];
	if(res!=-1)
		return res;
	res=0;
	if(a+b+c+d+e==0)
		return res=1;
	if(a)
		res=add(res,mul(a-(pre==2),dfs(a-1,b,c,d,e,1)));
	if(b)
		res=add(res,mul(b-(pre==3),dfs(a+1,b-1,c,d,e,2)));
	if(c)
		res=add(res,mul(c-(pre==4),dfs(a,b+1,c-1,d,e,3)));
	if(d)
		res=add(res,mul(d-(pre==5),dfs(a,b,c+1,d-1,e,4)));
	if(e)
		res=add(res,mul(e,dfs(a,b,c,d+1,e-1,5)));
	return res;
}
int main()
{
	k=read();
	for(int i=1;i<=k;++i)
		{
			col[i]=read();
			n+=col[i];
			++t[col[i]];
		}
	memset(f,-1,sizeof f);
	int ans=dfs(t[1],t[2],t[3],t[4],t[5],0);
	printf("%d\n",ans);
	return 0;
}
posted @ 2018-12-05 19:15  jklover  阅读(66)  评论(0编辑  收藏