CF451 - Devu and Flowers
n倍经验题:
感觉算法进阶指南的题目有空还是要都刷一下
题意: 有n种花,每种花最多有 f i f_i fi朵,问要取s朵有多少种取法(对1e9+7取模)
$ n \leq 20, s \leq 1e14, f_i \leq 1e12$
思路:
(其实感觉网上题解随便一搜都讲的比我好我就记录整理给自己看看(
状压+组合数 (隔板法)+容斥原理
如果每个f都无限大,则 a n s = C n + s − 1 n − 1 ans = C^{n - 1}_{n + s - 1} ans=Cn+s−1n−1(隔板法)
但这样取会有花超过该种花总数上限,但考虑某种超过上限的花,该花至少取了f+1,即不合法地取该花的总数为 C ( n + s − 1 ) − ( f − 1 ) n − 1 C^{n - 1}_{(n + s - 1) - (f - 1)} C(n+s−1)−(f−1)n−1,(相当于这种花已经取了f+1,其余(n + s - 1) - (f - 1)继续隔板)
结合到n的范围,这时候容斥就来了
最终的 a n s = C n + s − 1 n − 1 − ∑ C n + s − 1 − f i − 1 n − 1 + ∑ C n + s − 1 − f i − 1 − f j − 1 n − 1 … ans = C^{n - 1}_{n + s - 1} - \sum C^{n - 1}_{n + s - 1 - f_i - 1} + \sum C^{n - 1}_{n + s - 1 - f_i - 1 - f_j - 1} \dots ans=Cn+s−1n−1−∑Cn+s−1−fi−1n−1+∑Cn+s−1−fi−1−fj−1n−1…
同样注意到n很小,C可以直接算,算的时候注意不要爆longlong
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr ll mod = 1e9 + 7;
constexpr ll N = 30;
ll f[N];
ll invn[N]; // n inv
inline void init() {
invn[0] = invn[1] = 1;
for (ll i = 2; i < N; ++i) {
invn[i] = (mod - mod / i) * invn[mod % i] % mod;
}
}
ll C(ll up, ll down) {
if (up > down) return 0;
if (up < 0 || down < 0) return 0;
ll res = 1;
for (ll i = down - up + 1; i <= down; ++i) {
res = res * (i % mod) % mod;
}
for (ll i = 1; i <= up; ++i) {
res = res * invn[i] % mod;
}
return res;
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
init();
ll n, s;
cin >> n >> s;
for (ll i = 0; i < n; ++i) {
cin >> f[i];
}
ll ans = 0;
for (ll st = 0; st < (1 << n); ++st) {
ll cnt = 0, down = n + s - 1, up = n - 1;
for (ll i = 0; i < n; ++i) {
if ((st >> i) & 1) {
cnt++;
down -= f[i] + 1;
}
}
// cout << cnt << " " << up << " " << down << endl;
if (cnt & 1) {
ans = (ans - C(up, down) + mod) % mod;
} else {
ans = (ans + C(up, down)) % mod;
}
}
cout << (ans + mod) % mod << endl;
return 0;
}