火题小战 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;
}