bzoj 5215: [Lydsy2017省队十连测]商店购物

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

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 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

Sample Output

6
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;
}

  

原文地址:https://www.cnblogs.com/JYYHH/p/8762128.html