AT2000 [AGC002F] Leftmost Ball
https://www.luogu.com.cn/problem/AT2000
400 篇 b l o g s 祭 \huge 400篇blogs祭 400篇blogs祭
考虑DP
一位位填显然不太行,考虑一次填完一种颜色
只要保证在任意时刻白球的个数>=已填的颜色数即可
设 f [ i ] [ j ] f[i][j] f[i][j]表示填了 i i i个白球,填了 j j j个颜色的方案数
转移分两种
- 填一个白球 f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i−1][j]
- 填一种球
f
[
i
]
[
j
]
+
=
f
[
i
]
[
j
−
1
]
∗
(
n
−
j
+
1
)
∗
(
n
k
−
i
−
(
j
−
1
)
(
k
−
1
)
−
1
k
−
1
−
1
)
f[i][j]+=f[i][j-1]*(n-j+1)*\binom{nk-i-(j-1)(k-1)-1}{k-1-1}
f[i][j]+=f[i][j−1]∗(n−j+1)∗(k−1−1nk−i−(j−1)(k−1)−1)
表示的是,从剩下的 n − j + 1 n-j+1 n−j+1种颜色中选一个,第一个空的位置填那种颜色,然后在剩下 n k − i − ( j − 1 ) ( k − 1 ) − 1 nk-i-(j-1)(k-1)-1 nk−i−(j−1)(k−1)−1填选 k − 1 − 1 k-1-1 k−1−1个位置放球
代码实现不难
code:
#include<bits/stdc++.h>
#define N 2005
#define ll long long
#define mod 1000000007
using namespace std;
ll qpow(ll x, ll y) {
ll ret = 1;
for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;;
return ret;
}
ll f[N][N], fac[N * N], ifac[N * N];
void init(int n) {
fac[0] = 1;
for(int i = 1; i <= n; i ++) fac[i] = fac[i - 1] * i % mod;
ifac[n] = qpow(fac[n], mod - 2);
for(int i = n - 1; i >= 0; i --) ifac[i] = ifac[i + 1] * (i + 1) % mod;
}
ll C(int n, int m) {
return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
int n, k;
int main() {
scanf("%d%d", &n, &k);
init(n * k);
if(k == 1) {
printf("1"); return 0;
}
f[0][0] = 1;
for(int i = 1; i <= n; i ++)
for(int j = 0; j <= i; j ++) {
f[i][j] = f[i - 1][j];
if(j) {
f[i][j] = (f[i][j] + (n - j + 1) * C(n * k - i - (j - 1) * (k - 1) - 1, k - 1 - 1) % mod * f[i][j - 1] % mod) % mod;
}
}
printf("%lld", f[n][n]);
return 0;
}