「AGC 002F」Leftmost Ball

nn 种颜色的球,标号 11nn ,每种颜色有 kk 个。将 nknk 个球随机排列后,将每种颜色的第一个球涂成颜色 00 ,求最终可能得到的颜色序列的方案数。

Constraints

1n,k20001\leq n,k \leq 2000

Solution

f(i,j) (ij)f(i,j)~(i\leq j) 表示已经放置了 ii 个编号为 00 的球与 jj 种第一次出现的位置最靠前的颜色的方案数。每次在当前的第一个空位放置一个颜色为 00 的球或是一种未出现的颜色的球。可得转移方程:

f(i,j)=f(i1,j)+(ni+(nj+1)(k1)1k2)(nj+1)f(i,j1)f(i,j)=f(i-1,j)+\binom{n-i+(n-j+1)\cdot(k-1)-1}{k-2}\cdot(n-j+1)\cdot f(i,j-1)

时间复杂度O(nk)O(nk)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const int N=2e3+5;
const int mod=1e9+7;
int n,m,fac[N*N],inv[N*N],f[N][N];
int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
void Mod(int& a,int b){a+=b;if(a>=mod)a-=mod;}
int power(int a,int b)
{
int ans=1;
while(b)
{
if(b&1)ans=1ll*ans*a%mod;
a=1ll*a*a%mod;b>>=1;
}
return ans;
}
int C(int n,int m){return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
int main()
{
n=read();m=read();
if(m==1){printf("1");return 0;}
fac[0]=1;
for(int i=1;i<=n*m;i++)fac[i]=1ll*fac[i-1]*i%mod;
inv[n*m]=power(fac[n*m],mod-2);
for(int i=n*m;i>=1;i--)inv[i-1]=1ll*inv[i]*i%mod;
f[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=i;j++)
{
f[i][j]=f[i-1][j];
if(!j)continue;
Mod(f[i][j],1ll*f[i][j-1]*(n-j+1)%mod*C(n-i+(n-j+1)*(m-1)-1,m-2)%mod);
}
printf("%d",f[n][n]);
return 0;
}