先給出比賽連結:
https://ac.nowcoder.com/acm/contest/88269
B 小雷的神奇電腦
Show Code B
C 崗位分配
已知每個志願者之間沒有區別,有不同的多個崗位且每個崗位至少有一個人,想到用組合數學隔板法來進行計數。
設 $ f_{n}^{m} $ 為n個崗位,m個人的分配情況數(注,任意崗位可沒有人)
然後即可透過隔板法求出 $ f_{n}^{m} = C_{n + m - 1}^{n - 1} $
下面以 $ f_{3}^{3} $ 為例:
總共有以下情況
(3,0,0) * 3
(2,1,0) * 6
(1,1,1) * 1
構造(3,0,0)可以 $ 1 2 3 \vert 0 \vert 0 $
構造(2,1,0)可以 $ 1 2 \vert 3 \vert 0 $
構造(1,1,1)可以 $ 1 \vert 2 \vert 3 $
本題每個崗位至少需要 $ a_i $ 個人那麼只需要把m減去 $ \sum\limits_{i = 1}^{n} a_i $
剩下的m就能用上面推出的 $ f_{n}^{m} $ 表示了
將i個候補加入崗位的貢獻是 $ f_{n}^{i} $
將之求和即可得到答案,就是 $ \sum\limits_{i = 0}^{m} f_{n}^{i} $
到這本題實際上已經完成了
但是還能繼續簡化
$
f_{n}^{m} = C_{n + m - 1}^{n - 1} = C_{n + m - 1}^{m}
$
$
\sum\limits_{i = 0}^{m} f_{n}^{i} = C_{n + i - 1}^{i}
$
\( 因為 C_{n}^{m} = C_{n - 1}^{m} + C_{n - 1}^{m - 1} \)
\( 所以 \sum\limits_{i = 0}^{m} f_{n}^{i} = C_{n + m}^{m} = f_{n + 1}^{m} ~~~(可用於字首和) \)
類似的:
\(
\sum\limits_{i = 1}^{n} f_{i}^{m} = C_{n + m + 1}^{m + 1} = f_{n + 1}^{m + 1}
\)
下面介紹求組合數的方法
1.楊輝三角法
\( 根據 C_{n}^{m} = C_{n - 1}^{m} + C_{n - 1}^{m - 1} ~~~ 可O(n^{2})預處理C_{n}^{m}的值 \)
2.公式法
\( 根據 C_{n}^{m} = \frac{n!}{m!(n-m)!} ~~~ 可O(n)預處理階乘,再透過公式計算 \)
下面給出AC程式碼,複雜度 $ O(n+m) $
Show Code C
constexpr ll mod = 998244353;
ll power(ll a , ll b) {
ll res = 1;
while (b) {
if (b & 1) {
res = res * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return res;
}
constexpr ll maxn = 10000;
ll fact[maxn + 2];
ll inv[maxn + 2];
void initfact() {
fact[0] = 1;
for (ll i = 1; i <= maxn; ++ i) {
fact[i] = fact[i - 1] * i % mod;
}
inv[maxn] = power(fact[maxn], mod - 2);
for (ll i = maxn - 1; i >= 1; -- i) {
inv[i] = inv[i + 1] * (i + 1ll) % mod;
}
}
ll C(int n, int m) {
if (m > n) {
return 0;
} else if (m == 0) {
return 1;
} else {
return fact[n] * inv[m] % mod * inv[n - m] % mod;
}
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
initfact();
ll n, m, summ = 0;
cin >> n >> m;
ll ans = 0;
vector a(n + 1);
for (int i = 1; i <= n; ++ i) {
cin >> a[i];
m -= a[i];
summ += a[i];
}
ans = C(n + m, m);
cout << ans << "\n";
}
D 簡單的素數
Show Code E
F 小雷的算式
Show Code F
H 聰明且狡猾的惡魔
Show Code H