【CF451E】Devu and Flowers

题目大意:求多重集合的组合数, (N le 1e14,M le 20)

题解:
考虑容斥原理,具体做法是枚举所有情况,即:枚举子集,第 i 位为 1 表示满足第 i 个条件,正负号采用 sign 进行判断。
对于本题的组合数来说,上指标过大,导致没办法预处理阶乘和逆元进行快速回答,不过下指标很小,可以按照定义进行枚举计算。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
typedef long long LL;
LL fpow(LL a,LL b,LL c){
	LL ret=1%c;
	for(;b;b>>=1,a=a*a%c)if(b&1)ret=ret*a%c;
	return ret;
}
LL C(LL n,LL m){
	if(n<m)return 0;
	m=min(m,n-m);
	LL ret=1,up=1,down=1;
	for(int i=1;i<=m;i++){
		up=up*(n-i+1)%mod;
		down=down*i%mod;
	}
	down=fpow(down,mod-2,mod);
	ret=ret*up%mod*down%mod;
	return ret;
}
LL Lucas(LL n,LL m){
	if(n<mod&&m<mod)return C(n,m);
	return C(n%mod,m%mod)*Lucas(n/mod,m/mod)%mod;
}

LL f[21],s,ans;
int n;

int main(){
	scanf("%d%lld",&n,&s);
	for(int i=1;i<=n;i++){
		scanf("%lld",&f[i]);
	}
	
	for(int i=0;i<1<<n;i++){
		LL tot=s,sign=1;
		for(int j=1;j<=n;j++)
			if(i>>(j-1)&1){
				tot-=(f[j]+1);
				sign=-sign;
			}
		if(tot<0)continue;
		ans=(ans+Lucas(tot+n-1,n-1)*sign)%mod;
	}
	printf("%lld
",(ans+mod)%mod);
	
	return 0;
} 
原文地址:https://www.cnblogs.com/wzj-xhjbk/p/10979082.html