[AGC002F] Leftmost Ball 题解
题意:
给你 $n$ 种颜色的球,每种颜色的球有 $k$ 个,把这 $n\times k$ 个球排成一排,
把每一种颜色的最左边出现的球涂成白色(初始球不包含白色),求有多少种不同的颜色序列。
答案对 $10^9+7$ 取模。
数据范围:
$1\leq n, k\leq 2000$。
可以看作 $n$ 个白球和 $n$ 种颜色的各 $k-1$ 个球。特判 $k=1$ 。
设 $f(i,j)$ 为放了 $i$ 个白球和 $j$ 种其他颜色的球的合法方案数。
这里规定 $j \le i$ ,称每个白球右侧到下一个白球前的位置为空位。考虑转移。
在左侧第一个空位放一个白球,从 $f(i-1,j)$ 处转移;
在左侧第一个空位放至少一个其他颜色的球,然后放完该颜色的球,
即从 $f(i,j-1)$ 处转移。考虑转移系数。首先从剩下 $n-j+1$ 种球里选一种放,
然后在剩下的 $nk-i-(j-1)(k-1)-1$ 个空位里放 $k-2$ 个(第一个空位至少放一个)
那么转移方程就是
时间复杂度 $\mathcal{O}(nk)$
代码:
#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
typedef long long ll;
const int mod = 1e9 + 7;
template<typename T> void up(T &x, T y) { x < y ? x = y : 0; }
template<typename T> void down(T &x, T y) { x > y ? x = y : 0; }
template<typename T> void add(T &x, T y) { (x += y) >= mod ? x -= mod : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(2e3 + 15))
#define N2 ((int)(4e6 + 15))
ll fac[N2], ifac[N2], dp[N][N];
ll C(int n, int m)
{
if(n < m || m < 0) return 0;
return fac[n] * ifac[n - m] % mod * ifac[m] % mod;
}
ll qpow(ll a, int b)
{
ll r = 1;
for(a %= mod; b; b >>= 1, a = a * a % mod)
if(b & 1) r = r * a % mod;
return r;
}
void init(int n)
{
fac[0] = 1;
rep(i, 1, n) fac[i] = fac[i - 1] * i % mod;
ifac[n] = qpow(fac[n], mod - 2);
Rep(i, n - 1, 0) ifac[i] = ifac[i + 1] * (i + 1) % mod;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int n, k; cin >> n >> k; init(N2 - 5);
if(k == 1) { return cout << "1\n", 0; }
dp[0][0] = 1;
rep(i, 1, n) rep(j, 0, i)
{
dp[i][j] = dp[i - 1][j];
if(j)
{
const ll _1 = dp[i][j - 1] * (n - j + 1) % mod;
const ll _2 = C(n - i + (n - j + 1) * (k - 1) - 1, k - 2);
add(dp[i][j], _1 * _2 % mod);
}
}
cout << dp[n][n] << '\n';
return 0;
}
参考文献: