Devu and Flowers(母函数\生成函数-二进制枚举)(Codeforces Round #258-Div. 2-451E)

23 篇文章 0 订阅
9 篇文章 0 订阅

文章目录

题目

D e v u Devu Devu 想用花去装饰他的花园,他已经购买了 n n n个箱子,第 i i i个箱子有 f i f_i fi朵花,在同一个的箱子里的所有花是同种颜色的(所以它们没有任何其他特征)。另外,不存在两个箱子中的花是相同颜色的。 现在 D e v u Devu Devu 想从这些箱子里选择 s s s朵花去装饰他的花园, D e v u Devu Devu 想要知道,总共有多少种方式从这些箱子里取出这么多的花?因为结果有可能会很大,结果需要对 1 0 9 + 7 10^9+7 109+7 取模。 D e v u Devu Devu 认为至少有一个箱子中选择的花的数量不同才是两种不同的方案。
输入:
第一行: n , s n,s n,s
第二行: f 1 , f 2 , . . . , f n f_1,f_2,...,f_n f1,f2,...,fn
数据范围: 1 ≤ n ≤ 20 , 0 ≤ s ≤ 1 0 14 , 0 ≤ f i ≤ 1 0 12 1\le n\le20,0\le s\le10^{14},0\le f_i\le10^{12} 1n20,0s1014,0fi1012

思路

我们考虑对于第 i i i 种花的生成函数:
F i ( x ) = 1 + x + x 2 + . . . + x f i = 1 − x f i + 1 1 − x F_i(x)=1+x+x^2+...+x^{f_i}=\frac{1-x^{f_i+1}}{1-x} Fi(x)=1+x+x2+...+xfi=1x1xfi+1
那么考虑总方案数的生成函数:
G ( x ) = ∏ i = 1 n F i ( x ) = ∏ i = 1 n 1 − x f i + 1 1 − x = ∏ i = 1 n ( 1 − x f i + 1 ) ( 1 − x ) n G(x)=\prod_{i=1}^nF_i(x)=\prod_{i=1}^n\frac{1-x^{f_i+1}}{1-x}=\frac{\prod_{i=1}^n(1-x^{f_i+1})}{(1-x)^n} G(x)=i=1nFi(x)=i=1n1x1xfi+1=(1x)ni=1n(1xfi+1)
然后就套路一波,因为:
H ( x ) = 1 + x + x 2 + . . . = 1 1 − x H(x)=1+x+x^2+...=\frac{1}{1-x} H(x)=1+x+x2+...=1x1
所以:
G ( x ) = ( ∏ i = 1 n ( 1 − x f i + 1 ) ) ∗ ( 1 + x + x 2 + . . . ) n G(x)=(\prod_{i=1}^n(1-x^{f_i+1}))*(1+x+x^2+...)^n G(x)=(i=1n(1xfi+1))(1+x+x2+...)n
那我们要求 G ( x ) G(x) G(x) s s s 项的系数 a s a_s as
我们发现右边就是不定方程的非负整数解方案数, g i = C i + n − 1 n g_i=C_{i+n-1}^{n} gi=Ci+n1n
左边由于 n n n 很小,所以考虑二进制枚举
我们记左边枚举所得结果为: c x p cx^p cxp那么右边应为 g s − p g_{s-p} gsp
那么
A n s = ∑ S c x p ∗ g s − p Ans=\sum^Scx^p*g_{s-p} Ans=Scxpgsp
g i g_i gi L u c a s Lucas Lucas定理计算即可

代码

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<ctime>
#include<bitset>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define ULL unsigned long long
LL read(){
	LL f=1,x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return f*x;
}
#define MAXN 20
#define Mod (LL)(1e9+7)
#define INF 0x3f3f3f3f
LL f[MAXN+5];
LL Pow(LL x,LL y){
	LL ret=1;
	while(y){
		if(y&1) ret=ret*x%Mod;
		x=x*x%Mod;
		y>>=1;
	}
	return ret;
}
LL Lucas(LL n,LL m){
	m=min(m,n-m);
	LL ret=1;
	while(n&&m){
		LL a=1,b=1,n0=n%Mod,m0=m%Mod;
		for(LL i=n0;i>=n0-m0+1;i--) a=a*i%Mod;
		for(LL i=1;i<=m0;i++) b=b*i%Mod;
		ret=ret*a%Mod*Pow(b,Mod-2)%Mod;
		n/=Mod,m/=Mod;
	}
	return ret;
}
int main(){
	int n=read();
	LL ans=0,s=read();
	for(int i=1;i<=n;i++)
		f[i]=read();
	for(int S=0;S<(1<<n);S++){
		LL cnt=s,sign=1;
		for(int i=1;i<=n;i++)
			if(S&(1<<(i-1)))
				cnt-=f[i]+1,sign=-sign;
		if(cnt<0) continue;
		ans=(ans+sign*Lucas(n+cnt-1,n-1)%Mod)%Mod;
	}
	printf("%lld\n",(ans+Mod)%Mod);
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值