火題小戰 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;
}