火题小战 A.玩个球 计数DP

火题小战 A.玩个球

题目描述

给你 \(n\) 种颜色的球,每个球有 \(k\) 个,把这 \(n\times k\) 个球排成一排,把每一种颜色的最左边出现的球涂成白色(初始球不包含白色),求有多少种不同的颜色序列,答案对 \(10^9+7\) 取模。

数据范围

对于\(30\%\)的数据,\(N \leq 3,K \leq 3\)
对于\(50\%\)的数据,\(N \leq 300,K \leq 300\)
对于另外\(20\%\)的数据,\(N = 2\)
对于\(100\%\)的数据,\(N \leq 2000,K \leq 2000\); 。

分析

观察这一道题的数据范围,我们可以使用 \(n^2\) 的做法

一个比较容易得出的结论是,如果你从左向右数的话,那么白球的数量一定大于等于经你已选择的颜色种类数

因此,我们设 \(f[i][j]\) 为当前已经放置了 \(i\) 个白球和 \(j\) 种其它颜色的球的合法方案数

下面我们考虑转移

首先, \(f[i][j]\) 一定可以由 \(f[i-1][j]\) 转移过来

因为如果当前你已经放置了 \(i-1\) 个白球和 \(j\) 种其他颜色的球,那么你在后面再放一个白球是没有影响的

接下来我们考虑 \(f[i][j-1]\) 的转移

首先,当前已经选择了 \(j-1\) 种颜色的球,那么我们还有 \(i-j+1\) 种颜色没有选择

对于某一种颜色的球,如果我们选择了它,那么我们必须从中选出一个球放在最前面

这样可以避免重复的情况

此时,这一种颜色的球已经被选择了 \(1\) 个放在最前面,还有 \(1\) 个被涂成了白球

还剩下 \(k-2\)

我们只需要从剩下的 \(n \times k-i-1- (j-1) \times (k-1)\)中选择 \(k-2\) 个位置就可以

我们默认之前的 \(j-1\) 种颜色的球已经放好

因此递推式为

\[f[i][j]=f[i-1][j]+f[i][j-1] \times (n-j+1) \times C_{n*k-i-1-(j-1)*(k-1)}^{k-2} \]

最后注意一下 \(k=1\) 时的特判

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
const int maxn=2005;
int f[maxn][maxn],ny[maxn*maxn],jc[maxn*maxn],jcc[maxn*maxn];
int getC(int n,int m){
	return jc[n]*jcc[m]%mod*jcc[n-m]%mod;
}
signed main(){
	int n,k;
	scanf("%lld%lld",&n,&k);
	if(k==1){
		printf("1\n");
		return 0;
	}
	ny[1]=1;
	for(int i=2;i<=4000000;i++){
		ny[i]=(mod-mod/i)*ny[mod%i]%mod;
	}
	jc[0]=1,jcc[0]=1;
	for(int i=1;i<=4000000;i++){
		jc[i]=jc[i-1]*i%mod;
		jcc[i]=jcc[i-1]*ny[i]%mod;
	}
	for(int i=0;i<=n;i++) f[i][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=i;j++){
			f[i][j]=f[i-1][j]%mod+f[i][j-1]%mod*(n-j+1)%mod*getC(n*k-i-1-(j-1)*(k-1),k-2)%mod;
		}
	}
	printf("%lld\n",f[n][n]%mod);
	return 0;

}
posted @ 2020-08-13 07:10  liuchanglc  阅读(152)  评论(1编辑  收藏  举报