题解:
很妙的一道题。
考虑怎么统计得不重不漏,每一种方案的除了白点的每一种颜色第一个出现的位置一定不同而且有顺序, 我们可以强制这个顺序为 1→n 1 → n , 最后乘上 n! n ! 。 同时,我们可以把每种方案的白点按照这个顺序赋颜色 (因为每一种方案这样赋颜色肯定合法,而且每一种合法的方案这样染色回去一定对应一种原始方案,所以他们的关系是双射)。
现在问题变为了:给定
n
n
个球,每种颜色的第一个球出现有顺序,第二个球出现也有顺序,且每种颜色的第一个球必须在第二个球之前,这个问题可以形象的表示为下面这个图的序个数:
我们知道求解一般
DAG
D
A
G
的
Top
T
o
p
序个数是
NP−completive
N
P
−
c
o
m
p
l
e
t
i
v
e
的,不过这个
DAG
D
A
G
图我们很好构造
DP
D
P
状态:
fi,j
f
i
,
j
表示下面的点位于第
i
i
列,上面的点位于第列的
Top
T
o
p
序数量,显然转移只有两种:
fi−1,j→fi,j,fi,j−1→fi,j
f
i
−
1
,
j
→
f
i
,
j
,
f
i
,
j
−
1
→
f
i
,
j
.具体的 :
fi,j=⎧⎩⎨⎪⎪fi,j−1+fi−1,j∗((k−2)+((i−1)∗k−(i−1−j))k−2)fi,j−10i>ji=ji>j
f
i
,
j
=
{
f
i
,
j
−
1
+
f
i
−
1
,
j
∗
(
(
k
−
2
)
+
(
(
i
−
1
)
∗
k
−
(
i
−
1
−
j
)
)
k
−
2
)
i
>
j
f
i
,
j
−
1
i
=
j
0
i
>
j
组合数在含义是两边的 Top T o p 序列无关,可以任意结合。
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int mod=1e9+7,N=2e3+50;
inline int add(int x,int y) {return (x+y>=mod)?(x+y-mod):(x+y);}
inline int mul(int x,int y) {return (LL)x*y%mod;}
inline int power(int a,int b) {
int rs=1;
for(;b;b>>=1,a=mul(a,a)) if(b&1) rs=mul(rs,a);
return rs;
}
int n,k,dp[N][N],fac[N*N*2],ifac[N*N*2];
inline int C(int x,int y) {return mul(ifac[x-y],mul(fac[x],ifac[y]));}
int main() {
cin>>n>>k; int l=2*n*k;
if(k==1) {puts("1"); return 0;}
fac[0]=1;
for(int i=1;i<=l;i++) fac[i]=mul(fac[i-1],i);
ifac[l]=power(fac[l],mod-2);
for(int i=l-1;~i;i--) ifac[i]=mul(ifac[i+1],i+1);
dp[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=i;j++) {
if(j==i) dp[i][j]=dp[i][j-1];
else dp[i][j]=add(dp[i][j-1],mul(dp[i-1][j],C(k-2+(i-1)*k-(i-j-1),k-2)));
}
printf("%d\n",mul(dp[n][n],fac[n]));
}