火题小战 A.玩个球

火题小战 A.玩个球

题目描述

给你 (n) 种颜色的球,每个球有 (k) 个,把这 (n imes 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 imes k-i-1- (j-1) imes (k-1))中选择 (k-2) 个位置就可以

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

因此递推式为

[f[i][j]=f[i-1][j]+f[i][j-1] imes (n-j+1) imes 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
");
		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
",f[n][n]%mod);
	return 0;

}
原文地址:https://www.cnblogs.com/liuchanglc/p/13494174.html