[BZOJ1042] [HAOI2008] 硬币购物

题目描述

Description

硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。

Input

第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000

Output

每次的方法数

Sample Input

1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900

Sample Output

4
27

题目分析:

这道题没有多组数据用分组背包就可搞,然而多组数据TLE->正解竟然用容斥原理.....

先用完全背包把不受限制的方案数都拿出来 然后再减去受限的方案数 具体怎么减,用容斥原理。

这里是有关容斥原理(+错排公式)的讲解(4集合的容斥原理公式)

抢个图:

也就是说:假设上图整个平面是不受限的方案数 那么我们需要圈内的东西减掉,并只能减去一次

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
long long f[101000];
int c[10],s,t;
int d[10];
long long n(int x)
{
	return x<0?0:f[x];
}
int main()
{
	f[0]=1;
	scanf("%d%d%d%d%d",&c[1],&c[2],&c[3],&c[4],&t);
	for(int i=1;i<=4;i++)
		for(int j=1;j<=100000;j++)
			if(j>=c[i]) f[j]+=f[j-c[i]];
    for(;t;t--)
    {
    	for(int i=1;i<=4;i++) scanf("%d",&d[i]);
    	scanf("%d",&s);
    	long long ans=f[s];
    	for(int i=1;i<=4;i++)
    		ans-=n(s-c[i]*(d[i]+1));
    	for(int i=1;i<=4;i++)
    		for(int j=i+1;j<=4;j++)
    			ans+=n(s-c[i]*(d[i]+1)-c[j]*(d[j]+1));
    	for(int i=1;i<=4;i++)
    		for(int j=i+1;j<=4;j++)
    			for(int k=j+1;k<=4;k++)
    				ans-=n(s-c[i]*(d[i]+1)-c[j]*(d[j]+1)-c[k]*(d[k]+1));
    	ans+=n(s-c[1]*(d[1]+1)-c[2]*(d[2]+1)-c[3]*(d[3]+1)-c[4]*(d[4]+1));
    	printf("%lld\n",ans);
    }
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注