2021 CCPC 威海赛区G题 Desserts
题解
有 n n n种类型的糖果(第 i i i种糖果有 a i a_i ai个)要分给 k ( k = [ 1 , m ] ) k(k=[1,m]) k(k=[1,m])个队伍,每个队伍在同一种类型的糖果中只能拿出一个。
现在要将所有的糖果发完,求有多少种方案。
数据范围满足 n , m ≤ 5 ⋅ 1 0 4 , ∑ i = 1 n a i ≤ 1 0 5 n,m\le 5\cdot 10^4,\sum_{i=1}^na_i\le 10^5 n,m≤5⋅104,∑i=1nai≤105。
思路
先考虑暴力的做法,当需要分给
k
k
k个队伍时,结果为
∏
i
=
1
n
C
k
a
i
\prod_{i=1}^{n}C_k^{a_i}
∏i=1nCkai(从
k
k
k个队伍中选出
a
i
a_i
ai个队分发第
i
i
i种糖果),组合数可以做预处理。
暴力去枚举
k
k
k时,复杂度为
O
(
m
⋅
n
)
O(m\cdot n)
O(m⋅n),会超时。
计算
∏
i
=
1
n
C
k
a
i
\prod_{i=1}^{n}C_k^{a_i}
∏i=1nCkai时,如果将相同的
a
i
a_i
ai放在一起,答案就变成了
∏
i
=
1
n
′
(
C
k
a
i
)
p
i
\prod_{i=1}^{n'}{(C_k^{a_i})}^{p_i}
∏i=1n′(Ckai)pi。
例如:
C
k
1
C
k
2
C
k
2
C
k
3
C
k
3
C
k
3
=
(
C
k
1
)
(
C
k
2
)
2
(
C
k
3
)
3
C_k^1C_k^2C_k^2C_k^3C_k^3C_k^3=(C_k^1)(C_k^2)^2(C_k^3)^3
Ck1Ck2Ck2Ck3Ck3Ck3=(Ck1)(Ck2)2(Ck3)3。
同时可以注意到题目中有一个条件是:
∑
i
=
1
n
a
i
≤
1
0
5
\sum_{i=1}^na_i\le 10^5
∑i=1nai≤105,那么
n
′
n'
n′最大为
1
0
5
\sqrt {10^5}
105。(想了半天也没想到这个条件可以这么用)
可以先预处理出来
p
p
p,然后计算时使用快速幂,这样就可以确保复杂度可以通过题目。
AC的代码
#include <bits/stdc++.h>
#define ll long long
#define pub push_back
#define sz(x) (int)(x).size()
using namespace std;
const int mod = 998244353;
const int N = 1e5 + 10;
ll power(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
ll inv(ll x) { return power(x, mod - 2); }
int n, m, a[N], p[N];
int vis[N];
ll fac[N], inv_f[N];
void init() {
fac[0] = 1;
for (int i = 1; i <= N - 10; i++) fac[i] = fac[i - 1] * i % mod;
inv_f[N - 10] = inv(fac[N - 10]);
for (int i = N - 11; i >= 0; i--) inv_f[i] = inv_f[i + 1] * (i + 1) % mod;
}
ll C(ll n, ll m) {
if (m > n) return 0;
return fac[n] * inv_f[m] % mod * inv_f[n - m] % mod;
}
int main() {
init();
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
vector<int> vec; // 最多sqrt(1e5)个
for (int i = 1; i <= n; i++) {
p[a[i]]++;
if (vis[a[i]]) continue;
vis[a[i]] = 1;
vec.pub(a[i]);
}
for (int k = 1; k <= m; k++) {
ll res = 1;
for (auto it : vec) res = res * power(C(k, it), p[it]) % mod;
cout << res << "\n";
}
return 0;
}