[HAOI2018]硬币购物

都这年头了,还有谁购物用硬币的吗?

题目大意

首先,你有四种面值的硬币

然后你会出门采购

每次出门,你都会带上一些硬币,每种硬币的个数给定

然后你买了的东西

问你有多少种付款的方案

分析

直接写裸的背包的时间复杂度直接炸天

那么正难则反是一个十分优秀的想法

就是说,可以不计代价的用总的方案减去不合法的方案

那么剩下的全是合法的了

那么,就要分别统计

这四种硬币构成的所有集合的不合法的方案

及当且仅当上述集合中的种类的硬币不满足条件是的总方案数

怎么求?

首先,可以用到二进制枚举子集,得到上述个集合

然后考虑容斥

可以得到,子集大小为奇数的容斥系数为,而偶数的容斥系数为

那么这部分可以表示如下:

inline void Solve()
{
        for (int i = 1; i < 16; ++i) {
            int size = tot;
            cke = 0;
            for (int state = i, j(0); state; ++j, state >>= 1)
                if (state & 1) cke ^= 1, size -= s[j] * (d[j] + 1);
            if (size >= 0) {
                if (cke & 1) ans -= f[size];
                else ans += f[size];
            }
        }
}

那么最后用整体减一下就可以了

code

signed main()
{
    for (int i = 0; i < 4; ++i) {
        s[i] = __read();
        for (int j = s[i]; j < maxn; ++j)
            f[j] += f[j - s[i]];
    }
    int T = __read();

    while (T--) {
        for (int i = 0; i < 4; ++i)
            d[i] = __read();

        int tot = __read(), cke(0);
        ll ans(f[tot]);
        Solve();
        printf ("%lld\n", ans);
    }
    system("pause");
}