5215: [Lydsy2017省队十连测]商店购物
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 129 Solved: 50
[Submit][Status][Discuss]
Description
在 Byteland一共开着 n家商店,编号依次为 1到 n,其中编号为1到 m的商店有日消费量上限,第 i家商店的日消
费量上限为wi。Byteasar每次购物的过程是这样的:依次经过每家商店,然后购买非负整数价格的商品,并在结账
的时候在账本上写上在这家商店消费了多少钱。当然,他在这家商店也可以什么都不买,然后在账本上写上一个0
。这一天, Byteasar日常完成了一次购物,但是他不慎遗失了他的账本。他只记得自己这一天一共消费了多少钱
,请写一个程序,帮助 Byteasar计算有多少种可能的账单。
Input
第一行包含三个正整数
n, m, k,分别表示商店的个数、有限制的商店个数以及总消费量。
第二行包含 m个整数,依次表示 w1;w2...wm。
1 ≤ m ≤ n,0≤ wi ≤ 300,1 ≤ n, k ≤ 5000000
m<=300
Output
输出一行一个整数,即可能的账单数,由于答案可能很大,请对1000000007取模输出。
Sample Input
3 2 8
2 1
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}。
HINT
6 种方案分别为:{0; 0; 8}; {1; 0; 7}; {2; 0; 6}; {0; 1; 7};
{1; 1; 6}; {2; 1; 5}。
HINT
Source
前面背包,后面组合算。
#include<bits/stdc++.h> #define ll long long using namespace std; const int ha=1000000007; const int maxn=10000000; int jc[maxn+5],ni[maxn+5]; int f[2][100005],n,m,K,tot; int ans=0,a[305],now,pre; inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x; } inline int ksm(int x,int y){ int an=1; for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha; return an; } inline void init(){ jc[0]=1; for(int i=1;i<=maxn;i++) jc[i]=jc[i-1]*(ll)i%ha; ni[maxn]=ksm(jc[maxn],ha-2); for(int i=maxn;i;i--) ni[i-1]=ni[i]*(ll)i%ha; } inline int C(int x,int y){ if(x<y) return 0; return jc[x]*(ll)ni[y]%ha*(ll)ni[x-y]%ha; } inline void dp(){ f[0][0]=1; for(int i=1;i<=m;i++){ tot+=a[i]; pre=now,now^=1; for(int j=0,sum=0;j<=tot;j++){ sum=add(sum,f[pre][j]); if(j>a[i]) sum=add(sum,ha-f[pre][j-a[i]-1]); f[now][j]=sum; } } } inline void calc(){ if(!n){ if(K<=tot) ans=f[now][K]; else ans=0; } else for(int i=min(K,tot);i>=0;i--) ans=add(ans,f[now][i]*(ll)C(K-i+n-1,K-i)%ha); } int main(){ init(); scanf("%d%d%d",&n,&m,&K); for(int i=1;i<=m;i++) scanf("%d",a+i); n-=m; dp(); calc(); printf("%d ",ans); return 0; }