# 火题小战 A.玩个球
火题小战 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\) 种颜色的球已经放好
因此递推式为
\]
最后注意一下 \(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;
}