1. 程式人生 > 實用技巧 ># 火題小戰 A.玩個球

# 火題小戰 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\) 種顏色的球已經放好

因此遞推式為

\[f[i][j]=f[i-1][j]+f[i][j-1] \times (n-j+1) \times 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\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;

}