版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
前言
神奇神奇真神奇
题目
赫克托是一个魁梧的粉刷匠,而且非常喜欢思考= =
现在,神庙里有N根排列成一直线的石柱,从1到N标号,长老要求用油漆将这些石柱重新粉刷一遍。赫克托有K桶颜色各不相同的油漆,第i桶油漆恰好可以粉刷Ci根石柱,并且,
(即粉刷N根石柱正好用完所有的油漆)。长老为了刁难赫克托,要求相邻的石柱颜色不能相同。
喜欢思考的赫克托不仅没有立刻开始粉刷,反而开始琢磨一些奇怪的问题,比如,一共有多少种粉刷的方案?为了让赫克托尽快开始粉刷,请你尽快告诉他答案,方案数模
。
数据范围:
思路
定义前缀和:
定义Dp状态:
表示前
种颜色刷完
个石柱,恰有
个相邻颜色相同石柱的方案数
我们考虑转移:
将当前颜色数
划分成
块方案数为
,那么会产生
个相邻颜色相同石柱,考虑将这些颜色块分成两种:破坏原有相邻颜色相同石柱的方案数和不破坏的方案数
于是我们要枚举出被破坏颜色相同石柱的个数
那么在就是组合
剩下的石柱考虑插入位置, 方案数为
最终答案就是
代码
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<climits>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define LL long long
#define ULL unsigned long long
using namespace std;
int read(){
int f=1,x=0;char c=getchar();
while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return f*x;
}
#define MAXC 6
#define MAXK 15
#define MAXN 90
#define INF 0x3f3f3f3f
#define Mod int(1e9+7)
inline int Add(int a,int b){
a+=b;
if(a>=Mod)
a-=Mod;
return a;
}
int C[MAXN+5][MAXN+5],f[MAXK+5][MAXN+5];
void Prepare(){
C[0][0]=1;
for(int i=1;i<=MAXN;i++){
C[i][0]=1;
for(int j=1;j<=MAXN;j++)
C[i][j]=Add(C[i-1][j],C[i-1][j-1]);
}
return ;
}
int main(){//f[i][j]:前i中颜色所涂s[i]个木板中,有j个相邻颜色相同的方案数
Prepare();
int T=read();
while(T--){
int K=read(),m=0;
f[0][0]=1;
for(int i=1;i<=K;i++){
int c=read();
for(int j=0;j<=m;j++)//颜色相邻相同数
if(f[i-1][j]){
for(int k=1;k<=c;k++)//当前颜色划分块数
for(int l=0;l<=min(k,j);l++)//l个块刚好插进颜色相同中的个数
f[i][j-l+c-k]=Add(f[i][j-l+c-k],1ll*f[i-1][j]*C[c-1][k-1]%Mod*C[j][l]*C[m-j+1][k-l]%Mod);
}
m+=c;
}
printf("%d\n",f[K][0]);
for(int i=1;i<=K;i++)
for(int j=0;j<=m-1;j++)
f[i][j]=0;
}
return 0;
}
后言
一道好题