1042: [HAOI2008]硬币购物 - BZOJ

时间:2022-07-20 06:44:13

Description

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

第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s
Output

每次的方法数
Sample Input
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900
Sample Output
4
27
数据规模
di,s<=100000
tot<=1000

每一次都做一次多重背包跟定是不行的,这个先预处理一下,然后可以用容斥原理O(1)回答

我们可以这样用容斥,先算出没有限制钱币数量的方案数,记为f[i],表示没有限制,总价值为i的方案数,然后减去至少一种钱币超过限制的方案数,加上至少两种钱币超过限制的方案数,减去至少三种钱币超过限制的方案数,最后加上四种钱币都超过限制的方案数,第i种钱币超过限制就是第i种钱币至少用了di+1个(例:第1种钱币超过限制的方案数就是f[s-c[1]*(d1+1)])

犯了一个傻逼错误

求f[i]的时候我是这么求的

     for i:= to maxs do
for j:= to do
inc(f[i],fn(i-c[j]));

然后理所当然的爆了int64

为了不重复计算方案,所以应该以j为阶段(话说动态规划的阶段是什么早就忘了),就像这样

     for j:= to  do
for i:= to maxs do
inc(f[i],fn(i-c[j]));

吐槽:

R:容斥原理是什么?我们学过吗?

X:学过啊

R:额,这算学过吗,我只记得老师叫我们用容斥写在100以内是2,3,5的倍数的数有多少,然后要我们查公式,这TM有什么意思,这个题有必要做吗,换个例题不行啊

当时我就想啊,容斥这么垃圾(不要打我......),我就直接跳过了,没想到容斥原来用处挺大的啊(当时讲的那个例题毫无吸引力好吗)

 const
maxs=;
var
f:array[..maxs]of int64;
c:array[..]of longint;
n:longint;
ans:int64; function fn(x:longint):int64;
begin
if x>= then exit(f[x]);
exit();
end; procedure main;
var
i,j,d1,d2,d3,d4,s:longint;
begin
for i:= to do
read(c[i]);
f[]:=;
for j:= to do
for i:= to maxs do
inc(f[i],fn(i-c[j]));
read(n);
for i:= to n do
begin
read(d1,d2,d3,d4,s);
ans:=fn(s);
dec(ans,fn(s-c[]*(d1+)));
dec(ans,fn(s-c[]*(d2+)));
dec(ans,fn(s-c[]*(d3+)));
dec(ans,fn(s-c[]*(d4+)));
inc(ans,fn(s-c[]*(d1+)-c[]*(d2+)));
inc(ans,fn(s-c[]*(d1+)-c[]*(d3+)));
inc(ans,fn(s-c[]*(d1+)-c[]*(d4+)));
inc(ans,fn(s-c[]*(d2+)-c[]*(d3+)));
inc(ans,fn(s-c[]*(d2+)-c[]*(d4+)));
inc(ans,fn(s-c[]*(d3+)-c[]*(d4+)));
dec(ans,fn(s-c[]*(d2+)-c[]*(d3+)-c[]*(d4+)));
dec(ans,fn(s-c[]*(d1+)-c[]*(d3+)-c[]*(d4+)));
dec(ans,fn(s-c[]*(d1+)-c[]*(d2+)-c[]*(d4+)));
dec(ans,fn(s-c[]*(d1+)-c[]*(d2+)-c[]*(d3+)));
inc(ans,fn(s-c[]*(d1+)-c[]*(d2+)-c[]*(d3+)-c[]*(d4+)));
writeln(ans);
end;
end; begin
main;
end.