【背包+组合计数】BZOJ5215 [Lydsy2017省队十连测] 商店购物

【题目】
BZOJ
n n n个非负整数,其中前 m m m个不超过 w i w_i wi,已知所有数字和为 k k k,求方案数。
m , w i ≤ 300 , n , k ≤ 5 × 1 0 6 m,w_i\leq 300,n,k\leq 5\times 10^6 m,wi300,n,k5×106

【解题思路】
m m m个数可以背包算出方案数,后面的数字用隔板法可以直接求出方案。
背包的时候可以发现贡献的是连续一段,所以做一个差分即可。
m , w m,w m,w同阶,复杂度 O ( n + m 3 ) O(n+m^3) O(n+m3)
神tm还卡常,预处理需要处理刚好够才能过。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e7+100,mod=1e9+7;
int n,m,K,mx,t;
ll ans,f[N],fac[N],ifac[N];
ll qpow(ll x,ll y){ll res=1;for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod;return res;}
ll C(int x,int y){if(x<0)return 1;if(y<0||x<y)return 0;return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}//wrong because nopd n<0
void up(ll &x,ll y){x+=y;if(x>=mod)x-=mod;}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("BZOJ5215.in","r",stdin);
	freopen("BZOJ5215.out","w",stdout);
#endif
	scanf("%d%d%d",&n,&m,&K);f[0]=1;t=m*300;
	fac[0]=1;for(int i=1;i<=n+K;++i)fac[i]=fac[i-1]*i%mod;
	ifac[n+K]=qpow(fac[n+K],mod-2);for(int i=n+K-1;~i;--i)ifac[i]=ifac[i+1]*(i+1)%mod;
	
	for(int i=1,x;i<=m;++i)
	{
		scanf("%d",&x);mx+=x;
		for(int j=mx-x;~j;--j) up(f[j+x+1],mod-f[j]);
		for(int j=1;j<=t;++j) up(f[j],f[j-1]);
	}
	//for(int i=0;i<=mx;++i) printf("%lld ",f[i]); puts("");
	n-=m;
	for(int i=0;i<=K;++i) up(ans,f[i]*C(n+(K-i)-1,n-1)%mod);
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值