【bzoj1042】[HAOI2008]硬币购物

1042: [HAOI2008]硬币购物

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 2709   Solved: 1654
[ Submit][ Status][ Discuss]

Description

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

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

HINT

Source

[ Submit][ Status][ Discuss]




。。。。。。容斥原理


设f[i]为总钱数为i,且硬币数量无限制的方案数

先把f[i]跑出来

然后我们对于每一个询问,可以根据容斥原理

ans = f[s] - c1超过d1个的方案 - c2超过d2个的方案 - ...... + c1超过d1个并且c2超过d2个的方案 + ........


就是奇数的减掉,偶数的加起来


那么怎么求ci超过ci个的方案呢?

其实我们可以强制它买di + 1个,那么其实这个方案就是为f[s - (di + 1) * ci]


PS.对于多个同时超过的需要为f[s - Σ(di  + 1) * ci],蒟蒻一时智障居然把他们乘了起来螺旋爆炸。。。。。


代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
 
typedef long long LL;

const int INF = 2147483647;
const int maxn = 100100;

LL f[maxn],c[10],d[10];
LL n,ans,tot;

inline LL getint()
{
    LL ret = 0,f = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
        ret = ret * 10 + c - '0',c = getchar();
    return ret * f;
}

int main()
{
	for (int i = 1; i <= 4; i++) c[i] = getint();
	n = getint();
	f[0] = 1;
	for (int i = 1; i <= 4; i++)
		for (int j = c[i]; j <= 100000; j++) f[j] += f[j - c[i]];
	for (int i = 1; i <= n; i++)
	{
		d[1] = getint(); d[2] = getint(); d[3] = getint(); d[4] = getint();
		tot = getint();
		ans = f[tot];
		for (int s = 1; s <= 15; s++)
		{
			int x = s,j = 0;
			LL sum = tot,cnt = 0;
			while (x)
			{
				j++;
				if (x & 1)
				{
					cnt++;
					sum -= (d[j] + 1) * c[j];
				}
				x >>= 1;
			}
			if (cnt & 1) ans -= sum < 0 ? 0 : f[sum];
			else ans += sum < 0 ? 0 : f[sum];
		}
		printf("%lld\n",ans);
	}
    return 0;
}


你可能感兴趣的:(动态规划,容斥原理)