1042: [HAOI2008]硬币购物
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 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
3 2 3 1 10
1000 2 2 2 900
Sample Output
4
27
27
HINT
Source
。。。。。。容斥原理
设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<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<algorithm>
#include<cstdlib>
#include<stack>
#include<cstring>
#include<cmath>
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;
}