AT2000 [AGC002F] Leftmost Ball

https://www.luogu.com.cn/problem/AT2000

400 篇 b l o g s 祭 \huge 400篇blogs祭 400blogs

考虑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[i1][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][j1](nj+1)(k11nki(j1)(k1)1)
    表示的是,从剩下的 n − j + 1 n-j+1 nj+1种颜色中选一个,第一个空的位置填那种颜色,然后在剩下 n k − i − ( j − 1 ) ( k − 1 ) − 1 nk-i-(j-1)(k-1)-1 nki(j1)(k1)1填选 k − 1 − 1 k-1-1 k11个位置放球
    代码实现不难
    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;
}
posted @ 2021-11-01 07:44  lahlah  阅读(41)  评论(0编辑  收藏  举报