G_product
题意 : 给出n, k, D. 求 D ! ∏ i = 1 n ( a i + k ) ! ( ∑ i = 1 n a i = D ) \frac{D!}{\prod_{i = 1}^{n}(a_i + k)!} (\sum_{i=1}^{n} a_i = D) ∏i=1n(ai+k)!D!(∑i=1nai=D)
G-Product_2021牛客暑期多校训练营4 (nowcoder.com)
参考了欣君的题解。【算法题解】2021牛客暑期多校训练营#4_哔哩哔哩_bilibili
首先当
k
=
=
0
k == 0
k==0(没有k) 有个结论:
∑
a
i
≥
0
,
∑
a
i
=
D
∏
i
1
a
i
=
n
D
D
!
\sum_{a_i \geq 0, \sum a_i = D} \prod_i \frac{1}{a_i} = \frac{n^D}{D!}
ai≥0,∑ai=D∑i∏ai1=D!nD
这里可以先考虑这个式子: D ! ∏ i = 1 n a i ! = n D \frac{D!}{\prod_{i = 1}^{n}a_i!} = n ^D ∏i=1nai!D!=nD, 问题转化为D个球染1-n种颜色, a 1 … a n a_1 \dots a_n a1…an 表示染了每种颜色的数量
题 ⇒ D ! ∑ a i ≥ k , ∑ a i = D + n k ∏ i 1 a i ! 题 \Rightarrow D! \sum_{a_i \geq k, \sum a_i = D+nk} \prod_i \frac{1}{a_i!} 题⇒D!ai≥k,∑ai=D+nk∑i∏ai!1
先计算这个式子:
D
!
∑
a
i
≥
0
,
∑
a
i
=
D
+
n
k
∏
i
1
a
i
!
=
n
D
+
n
k
(
D
+
n
k
)
!
D
!
D! \sum_{a_i \geq 0, \sum a_i = D+nk} \prod_i \frac{1}{a_i!} \\ =\frac{n^{D +nk}}{(D+nk)!} D!
D!ai≥0,∑ai=D+nk∑i∏ai!1=(D+nk)!nD+nkD!
再将
a
i
<
k
a_i < k
ai<k 的部分容斥掉 :
考虑哪些 a i < k a_i < k ai<k ,枚举 a i a_i ai 的值, 但 n, D 不同, 需要DP
设 d p i , j dp_{i, j} dpi,j 表示将j个不同的球分成i组,每组不超过k-1个的方案数, d p i , j = ∑ t = 0 k − 1 d p i − 1 , j − t ( t j ) dp_{i, j} = \sum _{t = 0}^{k - 1} dp_{i - 1, j - t} \tbinom{t}{j} dpi,j=∑t=0k−1dpi−1,j−t(jt), (这部分可以预处理,虽然D很大,但n ,k 很小)
注意转化后的 a i a_i ai 是 ≥ k \ge k ≥k的 ,
枚举有i个不合法的 a i a_i ai,这些 a i a_i ai的和为j,再对这部分分配一下,可以得到 ( i n ) ( j D + n k ) d p i , j \tbinom{i}{n} \tbinom{j}{D + nk} dp_{i , j} (ni)(D+nkj)dpi,j
剩余的都满足 a i ≥ k a_i \ge k ai≥k , 这部分可以用隔板法直接求,
一共有n - i 组, D+nk - j 个球,每组球超过k个 的方案数
( n − i − 1 D − j ) \tbinom{n- i - 1}{D- j} (D−jn−i−1)
D很大好像不大好处理(
剩余的部分可以任意取,于是直接列一下就是$ (n - i) ^ {D+ nk - j}$, 因为会有重复计算,这里要容斥一下 ( − 1 ) i (- 1)^{i} (−1)i
结合一下: ∑ i ∑ j ( − 1 ) i ( i n ) ( j D + n k ) d p i , j ( n − i ) D + n k − j \sum_{i} \sum_{j}(- 1)^{i} \tbinom{i}{n} \tbinom{j}{D + nk} dp_{i , j} (n - i) ^ {D+ nk - j} ∑i∑j(−1)i(ni)(D+nkj)dpi,j(n−i)D+nk−j
然后把 D ! ( D + n k ) ! \frac{D!}{(D+nk)!} (D+nk)!D! 乘上去
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 60;
const ll M = 2510;
const ll mod = 998244353;
ll dp[N][M], c[M][M], cc[M];
ll n, d, k;
inline ll qpow(ll x, ll p) {
ll res = 1;
while (p) {
if (p & 1) res = (res * x) % mod;
x = (x * x) % mod;
p >>= 1;
}
return res;
}
inline void init() {
for (ll i = 0; i < M; ++i) {
c[i][0] = 1;
for (ll j = 1; j <= i; ++j) {
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
}
dp[0][0] = 1;
for (ll i = 1; i < N; ++i) {
for (ll j = 0; j < M; ++j) {
for (ll t = 0; t < k; ++t) {
if(j < t) continue;
dp[i][j] = (dp[i][j] + dp[i - 1][j - t] * c[j][t]% mod) % mod;
}
}
}
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> k >> d;
init();
ll ans = 0;
cc[0] = 1;
for (ll i = 1; i < M; ++i) {
cc[i] = cc[i - 1] * (d + n * k - i + 1 + mod) % mod * qpow(i, mod - 2) % mod;
}
for (ll i = 0; i <= n; ++i) {
ll tmp = 0;
for (ll j = 0; j <= i * (k - 1); ++j) {
tmp = (tmp + cc[j] * dp[i][j] % mod * qpow(n - i, d + n * k - j) % mod) % mod;
}
tmp = tmp * c[n][i] % mod;
if (i & 1) ans = (ans - tmp + mod) % mod;
else ans = (ans + tmp) % mod;
}
for (ll i = d + 1; i <= d + n * k; ++i) {
ans = ans * qpow(i, mod - 2) % mod;
}
cout << ans << endl;
return 0;
}