• BZOJ1042 [HAOI2008]硬币购物


    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

    题解

    令$f_i$表示不限使用四种硬币时付i元的方案数(就是完全背包)。预处理$f_{0..100000}$。

    询问时,利用容斥原理,答案等于不限使用硬币的方案数-有一种硬币必须使用$d_i$次以上的方案数+有两种硬币必须使用$d_i$次以上的方案数-有三种硬币必须使用$d_i$次以上的方案数+有四种硬币必须使用$d_i$次以上的方案数。

    枚举哪几种硬币一定使用$d_i$枚以上,计算时由于一定使用至少$d_i+1$次,只需要将$s$减去$(d_i+1)*c+i$再求方案数。

    每次需要询问16次,时间复杂度$O(16)$.

    总时间复杂度$O(100000+16tot)$(在渐进意义上它是和$O(tot)$一样的,但在wys意义上不是)。

    #include <cstdio>
    typedef long long LL;
    const int N = 100050;
    LL f[N], c[4];
    LL s, d[4];
    LL calc(int S) {
      LL t = s;
      int sign = 1;
      for (int i = 0; i < 4; ++i)
        if ((S >> i) & 1) {
          t -= (d[i] + 1) * c[i];
          sign = -sign;
        }
      if (t < 0) return 0;
      return sign * f[t];
    }
    int main() {
      f[0] = 1;
      for (int i = 0; i < 4; ++i) {
        scanf("%lld", &c[i]);
        for (int j = c[i]; j < N; ++j)
          f[j] += f[j - c[i]];
      }
      int T;
      scanf("%d", &T);
      while (T--) {
        scanf("%lld%lld%lld%lld%lld", &d[0], &d[1], &d[2], &d[3], &s);
        LL ans = 0;
        for (int i = 0; i < 16; ++i) ans += calc(i);
        printf("%lld
    ", ans);
      }
      return 0;
    }
    

      

  • 相关阅读:
    链表相交
    环路检测
    lambada表达式对集合的过滤和相互转换
    lambda表达式对集合的遍历
    centos7常用命令
    小程序文件
    扫码登录
    位操作
    使用json-lib转换对象为字符串时的特殊处理
    javac 编译异常总结
  • 原文地址:https://www.cnblogs.com/y-clever/p/6999141.html
Copyright © 2020-2023  润新知