[题解]CF451E Devu and Flowers
思路
Part 1 简单版
我们先想一想,如果每一种花有无限个,有多少种选法。
我们先假设第 \(i\) 种花选 \(x_i\) 支,那么,我们可以得出以下式子:
\[ \sum_{i = 1}^{i = n}x_i = m
\]
然后,我们根据隔板法,得出答案:\(C_{n + m + 1}^{n - 1}\)。
Part 2 本题
对于这题,拥有了一个 \(x_i \leq a_i\) 的条件,比较棘手,于是,我们可以通过容斥原理的方法来做。
这里,我们可以用正难则反的思想,求出至少有一个 \(x_i > a_i\) 的数量。
我们定义 \(S_i\) 表示不满足 \(x_i \leq a_i\) 的数量。
那么,我们的答案就是:\(C_{n + m - 1}^{n - 1} - |S_1 \cup S_2 \dots \cup S_n|\)。
通过容斥原理,得:$C_{n + m - 1}^{n - 1} - |S_1| - |S_2| - \dots + |S_1 \cap S_2| + \dots $。
那么,我们的 \(S_i\) 怎么求呢?
我们可以先假设 \(x_i\) 为 \(a_i + 1\),因此,我们的 \(S_i\) 就为 \(C_{n + m - a_i - 2}^{n - 1}\)。
例如:\(|S_1| = C_{n + m - a_1 - 2}^{n - 1},|S_1 \cap S_2| = C_{n + m - a_1 - a_2 - 3}^{n - 1}\)。
然后,我们也是通过状态压缩的方式解决。
Code
#include <bits/stdc++.h>
#define int long long
#define re register
using namespace std;
const int N = 25,mod = 1e9 + 7;
int n,m,inv,ans,sum = 1;
int arr[N];
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
inline int exgcd(int a,int b,int &x,int &y){//拓展欧几里得算法求逆元
if (!b){
x = 1;
y = 0;
return a;
}
int d = exgcd(b,a % b,y,x);
y = y - a / b * x;
return d;
}
inline int C(int a,int b){//组合数
if (a < b) return 0;
int x = 1;
for (re int i = a;i > a - b;i--) x = i % mod * x % mod;
return x % mod * inv % mod;
}
signed main(){
n = read();
m = read();
for (re int i = 1;i <= n;i++) arr[i] = read();
for (re int i = 1;i <= n - 1;i++) sum = sum * i % mod;
int x,y;
exgcd(sum,mod,x,y);//求逆元
inv = (x + mod) % mod;
for (re int i = 0;i <= (1 << n) - 1;i++){//状态压缩枚举
int cnt = 0;
int a = n + m - 1,b = n - 1;
for (int j = 1;j <= n;j++){
if ((i >> j - 1) & 1){
cnt++;
a = a - (arr[j] + 1);
}
}
if (cnt & 1) ans = (ans - C(a,b)) % mod;
else ans = (ans + C(a,b)) % mod;
}
printf("%lld",(ans + mod) % mod);
return 0;
}

浙公网安备 33010602011771号