【AGC002F】 Leftmost Ball 题解 (朴素 dp)
朴素 dp
Solution
题面看起来很难——不同的两种排序方案,可能染色之后对应的是同种状态。
那就不妨反向考虑——从最终状态,倒退它能指向多少种初始状态。
根据题意,一种颜色对应着一个白球,这引导着我们从每种颜色的角度考虑,即每次考虑把当前颜色都放进去。
然后你就能根据强大的经验设计出 dp 的策略:从左往右放,每次对最左边的一个空位,要么放一个白球,要么放一个有颜色的球,同时把该种颜色剩下的球都放到后面的位置去。
即,定义 \(f_{i,j}\) 表示当前有 \(i\) 个白球,并且已经把前 \(j\) 种颜色(假设我们把 \(k\) 中颜色的球按照颜色排了序)的球全部放好了的方案数。故当且仅当 \(i\geq j\) 时,状态合法。
根据策略:要么放一个白球,要么放一种颜色,转移就很显然了。
注意答案最后要乘上 \(n!\),表示颜色的不同排序方案。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
#define per(i, a, b) for(register int i = a; i >= b; --i)
const int maxn = 2005, mod = 1e9 + 7;
int f[maxn][maxn], n, k;
int fac[maxn * maxn], inv[maxn * maxn];
inline int pow(int x, int p){ int res = 1;
while(p){ if(p & 1) res = res * x % mod;
p /= 2, x = x * x % mod;
} return res;
}
inline int C(int t, int m){
if(t < m or t < 0 or m < 0) return 0;
return fac[t] * inv[m] % mod * inv[t - m] % mod;
}
signed main(){ scanf("%lld%lld", &n, &k);
if(k == 1) return printf("1"), 0;
fac[0] = 1; rep(i, 1, n * k) fac[i] = fac[i - 1] * i % mod;
inv[n * k] = pow(fac[n * k], mod - 2);
per(i, n * k - 1, 1) inv[i] = inv[i + 1] * (i + 1) % mod;
f[0][0] = 1, inv[0] = 1;
rep(i, 1, n) rep(j, 0, i){
if(j <= i - 1) f[i][j] = (f[i][j] + f[i - 1][j]) % mod;
if(j) f[i][j] = (f[i][j] + C(n * k - (j - 1) * (k - 1) - i - 1, k - 2) * f[i][j - 1] % mod) % mod;
} return printf("%lld", fac[n] * f[n][n] % mod), 0;
}
感谢阅读。

浙公网安备 33010602011771号