【BZOJ】1042: [HAOI2008]硬币购物

1042: [HAOI2008]硬币购物

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 3307  Solved: 2075
[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

Sample Output

4
27

HINT

 

Source

 
[Submit][Status][Discuss]


HOME Back

时隔多年(?)终于搞懂了第一道容斥题QwQ!!特此纪念。

首先我们可以做一次完全背包,每种硬币无限制地用,统计出方案数。然后我们就会发现多计入了一些不合法的情况,就是第$i$种硬币用了超出$d[i]$的数量的方案数。我们要统计所有不合法的情况,就是第一种硬币不合法的方案数+第二种硬币不合法的方案数+第三种硬币+第四种硬币-第一和第二-第二和第三...这就是奇加偶减的容斥!而我们是用所有情况减去不合法的情况,在$dfs$容斥中反过来就可以了。

【注意】$ans$最开始是0,因为在容斥中就会走到每一种硬币不合法的情况都不减去的情况,这时就是所有情况的总数。

代码中的$k$表示的就是当前减去了几个物品不合法的数量,奇加偶减。

$sum$是当前剩余需要填满的钱数,那么$f[sum-(d[i]+1)*c[i]]$表示的是第$i$种钱币用了$d[i]+1$填满$sum$的方案数,即这个硬币使用不合法的方案数。(强制使第$i$种硬币不合法

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;

int c[5], tot, d[5];
ll ans, f[100005];

void dfs ( int dep, int k, int sum ) {
    if ( sum < 0 ) return ;
    if ( dep == 5 ) {
        if ( k & 1 ) ans -= f[sum];
        else ans += f[sum];
        return ;
    }
    dfs ( dep + 1, k + 1, sum - ( d[dep] + 1 ) * c[dep] );
    dfs ( dep + 1, k, sum );
}

int main ( ) {
    for ( int i = 1; i <= 4; i ++ ) scanf ( "%d", &c[i] );
    scanf ( "%d", &tot );
    f[0] = 1;
    for ( int i = 1; i <= 4; i ++ )
        for ( int j = c[i]; j <= 100000; j ++ )
            f[j] += f[j-c[i]];
    while ( tot -- ) {
        for ( int i = 1; i <= 4; i ++ ) scanf ( "%d", &d[i] );
        int s;
        scanf ( "%d", &s );
        ans = 0;
        dfs ( 1, 0, s );
        printf ( "%lld\n", ans );
    }
    return 0;
}

 

posted @ 2018-08-22 21:09  Wans_ovo  阅读(...)  评论(... 编辑 收藏