1. 程式人生 > 實用技巧 >H - Leftmost Ball

H - Leftmost Ball

AT原題

以前做過,現在來整理思路

因為白球是一種特殊的球只會存在於一種顏色所有球的最前面,所以我們考慮把白球和其他球分開來

\(dp[i][j]\)表示我們已經放置了\(i\)個白球和\(j\)種顏色的球的方案數,顯然必須滿足\(j \leq i\)

轉移有兩種
1.上一次放了一個白球
2.上一次放了一種顏色的球,即放了\(k-1\)個相同顏色的球

放白球必須儘量靠前,為了避免重複,我們把白球放在第一個空位上。
如果不是在第一個空位上,而是在任意空位上,則有可能導致某個白球不是某種顏色球的第一個,
而且在DP時不同時候放的白球可能會導致最終結果重複。

放白球的貢獻為\(dp[i-1][j]\)

放有顏色的球,首先要從\(n - j + 1\)種顏色中選一個
然後我們剩下了\(n * k - i - (j - 1)*(k - 1)\)個空位,我們需要從中選\(k - 1\)個空位放我們的球,
但是貢獻並不是\(C_{n * k - i - (j - 1)*(k - 1)}^{k-1}\),而是\(C_{n * k - i - (j - 1)*(k - 1)-1}^{k-2}\)
原因還是重複,如果我們不取出一個球,把第一個空位佔掉,那麼對於任意兩次放有顏色球的操作,其結果可能是一樣的,但是計算會重複
假設一下,我有四個空位,每次放兩個有顏色的球,一共放兩次。
第一次,選紅色, 放2,3位; 第二次,選藍色,放1,4位

第一次,選藍色, 放1,4位; 第二次,選紅色,放2,3位
是一樣的,這就是不把第一個空位佔掉導致的重複

貢獻為\((n - j + 1) * C_{n * k - i - (j - 1)*(k - 1)-1}^{k-2}\)


#include<bits/stdc++.h>
using namespace std;

#define int long long

const int mod = 1e9 + 7;

int n,k;
int dp[2050][2050];

int fac[4000050],inv[4000050];
int ksm(int x,int y){
    int z = 1;
    while(y){
        if(y & 1) z = z * x % mod;
        y >>= 1;
        x = x * x % mod;
    }
    return z;
}
int C(int n,int m){
    if(n < m || m < 0) return 0;
    if(n == m || m == 0) return 1;
    return (fac[n] * inv[m] % mod) * inv[n - m] % mod;
}

signed main(){
    scanf("%lld%lld",&n,&k);
    
    fac[0] = 1;
    for(int i = 1; i <= 4000000; ++ i) fac[i] = fac[i - 1] * i % mod;
    inv[4000000] = ksm(fac[4000000],mod - 2);
    for(int i = 3999999; i >= 0; -- i) inv[i] = inv[i + 1] * (i + 1) % mod;
    
    if(k == 1) { puts("1"); return 0; } 
    
    dp[0][0] = 1;
    for(int i = 1; i <= n; ++ i){
        dp[i][0] = 1;
        for(int j = 1; j <= i; ++ j){
            dp[i][j] = (dp[i - 1][j] + dp[i][j - 1] * C(n * k - i - (j - 1) * (k - 1) - 1, k - 2) % mod * (n - j + 1) % mod ) % mod; 
        }
    }
    
    printf("%lld\n",dp[n][n]);
    return 0;
}