CF451E Devu and Flowers [组合数学 + 容斥原理]

传送门

首先, 将原问题简化, 假设每种花都有无数多, 从 n 个种类中选出 S 个的方案数

相当于一个长为S的序列, 将其分为n段, 所以答案为  C_{n+S-1}^{n-1}

考虑用总的减去不合法的, 因为 n 很小, 所以我们可以枚举不合法的集合

如果 i 不合法, 那么它至少选了 f[i] + 1 个, 剩下的可以随意乱选, 贡献就是 C_{n+S-(f[i]+1)-1}^{n-1}

所以对于当前枚举的集合 k , 贡献即为 C_{n+S-\sum_{i\in k}(f[i]+1)-1}^{n-1}

但是这保证了集合之外的就合法吗? 不

集合之内一定合法, 之外可能合法, 可能不合法, 于是只能容斥原理, 奇加偶减什么的

感性理解一下, 枚举到1不合法, 2也可能不合法, 会把1, 2都算上, 枚举2的时候也会把1算上

于是我们在枚举1, 2的时候减掉

组合数用 Lucas 就可以了, 由于上面的 n-1 很小, 所以直接暴力乘

#include<bits/stdc++.h>
#define N 25
using namespace std;
const int Mod = 1000000007;
typedef long long ll;
int n; ll s, f[N], ans;
ll mul(ll a, ll b){ return (a*b) % Mod;}
ll add(ll a, ll b){ return (a+b) % Mod;}
ll power(ll a, ll b){ ll ans = 1;
	for(;b;b>>=1){ if(b&1) ans = mul(ans, a); a = mul(a, a);}
	return ans;
}
ll C(int n, int m){
	if(n < m) return 0; m = min(m, n - m);
	ll up = 1, down = 1;
	for(int i=1; i<=m; i++){
		up = mul(up, n - i + 1); down = mul(down, i);
	} ll ans = mul(up, power(down, Mod-2));
	return ans; 
}
ll Lucas(ll n, ll m){
	if(n < Mod && m < Mod) return C(n, m);
	return mul(Lucas(n / Mod, m / Mod), C(n % Mod, m % Mod));
}
int main(){
	scanf("%d%lld", &n, &s);
	ans = Lucas(n + s - 1, n - 1);
	for(int i=1; i<=n; i++) scanf("%lld", &f[i]);
	for(int S = 1; S < (1<<n); S++){
		ll cnt = 0, res = s;
		for(int i=1; i<=n; i++){
			if(S & (1<<(i-1))){
				cnt++; res -= (f[i] + 1);
			}
		} 
		ll tmp = 0;
		if(res >= 0) tmp = Lucas(n + res - 1, n - 1);
		if(cnt & 1) ans = add(ans, Mod - tmp); else ans = add(ans, tmp);
	} printf("%lld", (ans % Mod + Mod) % Mod); return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值