粉刷匠(计数Dp,神奇状态)(2019-中山集训)[NOIP2013模拟]

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_37555704/article/details/98093195

前言

神奇神奇真神奇

题目

赫克托是一个魁梧的粉刷匠,而且非常喜欢思考= =
现在,神庙里有N根排列成一直线的石柱,从1到N标号,长老要求用油漆将这些石柱重新粉刷一遍。赫克托有K桶颜色各不相同的油漆,第i桶油漆恰好可以粉刷Ci根石柱,并且, C 1 + C 2 + C 3 C K = N C_1+C_2+C_3…C_K=N (即粉刷N根石柱正好用完所有的油漆)。长老为了刁难赫克托,要求相邻的石柱颜色不能相同。
喜欢思考的赫克托不仅没有立刻开始粉刷,反而开始琢磨一些奇怪的问题,比如,一共有多少种粉刷的方案?为了让赫克托尽快开始粉刷,请你尽快告诉他答案,方案数模 1 0 9 + 7 10^9+7
数据范围: K 15 , c i 6 , T 2000 K≤15,c_i≤6,T≤2000

思路

定义前缀和: s [ n ] = i = 1 n c [ i ] s[n]=\sum_{i=1}^nc[i]
定义Dp状态: f [ i ] [ j ] f[i][j] 表示前 i i 种颜色刷完 s [ i ] s[i] 个石柱,恰有 j j 个相邻颜色相同石柱的方案数
我们考虑转移:
将当前颜色数 c i c_i 划分成 k k 块方案数为 C ( c i 1 , k 1 ) C(c_i-1,k-1) ,那么会产生 C i k + 1 C_i-k+1 个相邻颜色相同石柱,考虑将这些颜色块分成两种:破坏原有相邻颜色相同石柱的方案数和不破坏的方案数
于是我们要枚举出被破坏颜色相同石柱的个数 l l 那么在就是组合 C ( j , l ) C(j,l)
剩下的石柱考虑插入位置, 方案数为 C ( s [ n ] j + 1 , j l ) C(s[n]-j+1,j-l)
最终答案就是 f [ K ] [ 0 ] f[K][0]

代码

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

后言

一道好题

猜你喜欢

转载自blog.csdn.net/qq_37555704/article/details/98093195