H - Leftmost Ball
阿新 • • 發佈:2020-07-20
以前做過,現在來整理思路
因為白球是一種特殊的球只會存在於一種顏色所有球的最前面,所以我們考慮把白球和其他球分開來
\(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; }