[HAOI2008] 硬币购物

[HAOI2008] 硬币购物

题目描述

共有 4 种硬币。面值分别为 c1,c2,c3,c4

某人去商店买东西,去了 n 次,对于每次购买,他带了 dii 种硬币,想购买 s 的价值的东西。请问每次有多少种付款方法。

输入格式

输入的第一行是五个整数,分别代表 c1,c2,c3,c4,n

接下来 n 行,每行有五个整数,描述一次购买,分别代表 d1,d2,d3,d4,s

输出格式

对于每次购买,输出一行一个整数代表答案。

样例 #1

样例输入 #1

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

样例输出 #1

4
27

提示

数据规模与约定

  • 对于 100% 的数据,保证 1ci,di,s1051n1000

[HAOI2008] 硬币购物

分析

第一眼看到题目,许多硬币组成一个价值的方案,限制个数,指定花费,这不就是多重背包求方案数吗?

然后注意到多次询问,每次都要做多重背包,就算是单调队列优化,复杂度也是 O(4ns)。显然出题人是专门卡多重背包的,多重背包的思路一定是没前途的。这样就要换一种思路。

+

先分别考虑每种硬币,有限制不好搞,可以考虑用全部状态减去非法状态。

fj 表示每个硬币可用无数次,得到价值为 j 的方案数。显然状态转移就是 fj=fj+fjc

非法状态就是硬币至少用了 d+1 个,将这 d+1 个硬币视为最后加上的,显然 fj 就是由 fj(d+1)×c 转移来的。

也就是说合法方案 ans=fjfj(d+1)×c

同理,四种硬币也是总状态减去非法状态,不过四种硬币的非法情况的方案数会有重叠,这就要用容斥原理了。

设第 i 种硬币的非法情况的方案数为 Ai

|A1A2A3A4|=(|A1|+|A2|+|A3|+|A4|)(|A1A2|+|A1A3|+|A1A4|+|A2A3|+|A2A4|+|A3A4|)+(|A1A2A3|+|A1A2A4|+|A1A3A4|+|A2A3A4|)|A1A2A3A4|

最后用 fj 减去即可。

总之一句话 ++

可以用壮压的方法枚举子集来进行容斥计算 state 的第 i 位为 1,则该状态有第 i+1 种硬币的限制。

code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=10;
int n,s;
int c[M],d[M];
ll f[N],ans;
int main ()
{
    for(int i=1;i<=4;i++) cin>>c[i];
    f[0]=1;
    for(int i=1;i<=4;i++)
        for(int j=c[i];j<N;j++)
            f[j]=f[j]+f[j-c[i]];
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        for(int i=1;i<=4;i++) cin>>d[i];
        cin>>s;
        ans=0;
        for(int i=0;i<(1<<4);i++)
        {
            int cnt=0;
            ll t=0;
            for(int j=1;j<=4;j++) if(i&(1<<(j-1))) cnt++,t+=c[j]*(d[j]+1);
            if(s>=t)
            {
                if(cnt&1) ans-=f[s-t];
                else ans+=f[s-t];
            }
        }
        cout<<ans<<"\n";
    }
    return 0;
}

部分语言参考:https://www.cnblogs.com/LLTYYC/p/10773808.html
https://www.luogu.com.cn/article/ywr15b0z

posted @   zhouruoheng  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示