BZOJ5215[Lydsy2017省队十连测] 商店购物

原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=5215

商店购物

Description

在 Byteland一共开着 n家商店,编号依次为 1到 n,其中编号为1到 m的商店有日消费量上限,第 i家商店的日消费量上限为wi。Byteasar每次购物的过程是这样的:依次经过每家商店,然后购买非负整数价格的商品,并在结账的时候在账本上写上在这家商店消费了多少钱。当然,他在这家商店也可以什么都不买,然后在账本上写上一个0。这一天, Byteasar日常完成了一次购物,但是他不慎遗失了他的账本。他只记得自己这一天一共消费了多少钱,请写一个程序,帮助 Byteasar计算有多少种可能的账单。

Input

第一行包含三个正整数 n, m, k,分别表示商店的个数、有限制的商店个数以及总消费量。

第二行包含 m个整数,依次表示 w1;w2…wm。

1 ≤ m ≤ n ≤ 300,0≤ wi ≤ 300,1 ≤ n, k ≤ 5000000

Output

输出一行一个整数,即可能的账单数,由于答案可能很大,请对1000000007取模输出。

Sample Input

3 2 8
2 1

Sample Output

6

HINT

6 种方案分别为:{0; 0; 8}; {1; 0; 7}; {2; 0; 6}; {0; 1; 7};

{1; 1; 6}; {2; 1; 5}。

题解

前面有限制的部分用背包统计贡献,后面没限制的直接用插板法算组合数。

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=305,mod=1e9+7,N=1e7+5;
int w[M],n,m,k,mx;
ll fac[N],inv[N],dp[M*M],sum,ans;
void in(){scanf("%d%d%d",&n,&m,&k);for(int i=1;i<=m;++i)scanf("%d",&w[i]),++w[i],sum+=w[i];}
ll power(ll x,ll p){ll ans=1;for(;p;x=x*x%mod,p>>=1)if(p&1)(ans*=x)%=mod;return ans;}
ll C(int n,int m){if(m<0)return 0;return fac[n]*inv[n-m]%mod*inv[m]%mod;}
void ac()
{
	fac[0]=dp[0]=inv[0]=1;
	for(int i=1;i<=m;++i)for(int j=sum;j>=w[i];--j)if(dp[j-w[i]])(dp[j]+=mod-dp[j-w[i]])%=mod;
	for(int i=1;i<=(mx=k+n);++i)fac[i]=fac[i-1]*i%mod;
	inv[mx]=power(fac[mx],mod-2);for(int i=mx-1;i>=1;--i)inv[i]=inv[i+1]*(i+1)%mod;
	for(int i=0;i<=sum;++i)(ans+=dp[i]*C(mx-i-1,k-i)%mod)%=mod;
	printf("%lld",ans);
}
int main(){in();ac();}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ShadyPi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值