#多重集组合数# CF451E Devu and Flowers

Title

CF451E Devu and Flowers


Solution

注意code中出现的锅
注意求组合数时通常会忘记判断一些边界的东西

不考虑 n i n_i ni的限制,从 S S S中任选 r r r个元素,方法数为 C k + r − 1 k − 1 C_{k+r-1}^{k-1} Ck+r1k1
根据容斥定理,至少有一种 a i a_i ai选取的数量超过 n i n_i ni限制的多重集共有
∣ ⋃ i = 1 k S i ∣ = ∑ i = 1 k C k + r − n i − 2 k − 1 − ∑ i = 1 k C k + r − n i − n j − 3 k − 1 + ⋯ + ( − 1 ) k + 1 C k + r − ∑ i = 1 k − ( k + 1 ) k − 1 \large \left | \bigcup_{i=1}^{k} S_{i} \right |=\sum_{i=1}^{k}\mathbb{C}_{k+r-n_i-2}^{k-1}-\sum_{i=1}^{k}\mathbb{C}_{k+r-n_i-n_j-3}^{k-1}+\cdots +(-1)^{k+1}\mathbb{C}_{k+r-\sum_{i=1}^{k}-(k+1)}^{k-1} i=1kSi=i=1kCk+rni2k1i=1kCk+rninj3k1++(1)k+1Ck+ri=1k(k+1)k1
满足所有限制的合法多重集共有 C k + r − 1 k − 1 − ∣ ⋃ i = 1 k S i ∣ \large C_{k+r-1}^{k-1}-\left | \bigcup_{i=1}^{k} S_{i} \right | Ck+r1k1i=1kSi

具体实现中,可以枚举 0 ∼ 2 N − 1 0\sim 2^N-1 02N1
x x x在二进制中的有 p p p为是 1 1 1的,表示 ( − 1 ) p C N + M − A i 1 − A i 2 − ⋯ − A i p − ( p + 1 ) N − 1 \large (-1)^p\mathbb{C}_{N+M-Ai_1-Ai_2-\cdots -Ai_p-(p+1)}^{N-1} (1)pCN+MAi1Ai2Aip(p+1)N1
其中特殊的把 x = 0 x=0 x=0看作 C k + r − 1 k − 1 C_{k+r-1}^{k-1} Ck+r1k1

注意题目中n的范围比较小,m的比较大
所以考虑用 L u c a s Lucas Lucas定理
C N M m o d   p = C N m o d   p M m o d   p ∗ C N / p M / p ( m o d   p ) \large \mathbb{C}_{N}^{M} mod\ p=\mathbb{C}_{Nmod\ p}^{Mmod\ p}*\mathbb{C}_{N/p}^{M/p}(mod\ p) CNMmod p=CNmod pMmod pCN/pM/p(mod p)

因为后面一项为 1 1 1,所以就相当于 N   m o d   p N\ mod\ p N mod p一下,避免爆炸。

逆元的话,可以线性求,似乎没有必要,毕竟N那么小
阶乘不太好预处理,就那样吧。


Code

#include<cstdio>
#include<algorithm>
#define ll long long 
#define rep(i,x,y) for(register ll i=x;i<=y;++i)
using namespace std; 
const ll mod=1e9+7,N=25; 
ll a[N],inv[N],ans; ll ff[N]; 
ll C(ll n,ll m){
	if (n<0||m<0||n<m) return 0; // it usually will be ignored
	n%=mod; // the special form of lucas theorem   
	if (n==0||m==0) return 1; // it usually will be ugnored 
	ll val=1; 
	rep(i,0,m-1) val=val*(n-i)%mod; //factorial
//	rep(i,1,m) val=val*inv[i]%mod; 
//	return val; 
	return val*ff[m]%mod; 
}/*
ll ksm(ll x,ll y){
	ll val=1; 
	for(;y;y>>=1,x=(x*x)%mod) if (y&1) val=(val*x)%mod; 
	return val; 
}*/
int main(){
	ll n,m; 
	scanf("%lld%lld",&n,&m); 
	rep(i,1,n) scanf("%lld",&a[i]); 
	ff[1]=inv[1]=1; 
	rep(i,2,n) inv[i]=(mod-mod/i)*inv[mod%i]%mod,ff[i]=ff[i-1]*inv[i]%mod; //linear inv
//	rep(i,1,n) inv[i]=ksm(i,mod-2);  //normal inv
	ans=C(n+m-1,n-1)%mod; 
	rep(i,1,(1<<n)-1) {
		ll t=n+m,p=0; 
		rep(j,0,n-1) if ((i>>j)&1) p++,t-=a[j+1]; 
		ans=(ans+((p&1)?-1:1)*C((t-(p+1)),n-1))%mod; 
	}
	printf("%lld",(ans+mod)%mod); 
	return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值