您的位置:首页 > 其它

[BZOJ 1042][HAOI2008]硬币购物

2016-03-14 21:25 302 查看
1042: [HAOI2008]硬币购物

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

容斥原理+背包。

byvoid:

设F[i]为不考虑每种硬币的数量限制的情况下,得到面值i的方案数。则状态转移方程为

F[i]=Sum{F[i-C[k]] | i-C[k]>=0 且 k=1..4}

为避免方案重复,要以k为阶段递推,边界条件为F[0]=1,这样预处理的时间复杂度就是O(S)。

接下来对于每次询问,奇妙的解法如下:根据容斥原理,答案为 得到面值S的超过限制的方案数 – 第1种硬币超过限制的方案数 – 第2种硬币超过限制的方案数 – 第3种硬币超过限制的方案数 – 第4种硬币超过限制的方案数 + 第1,2种硬币同时超过限制的方案数 + 第1,3种硬币同时超过限制的方案数 + …… + 第1,2,3,4种硬币全部同时超过限制的方案数。

当第1种硬币超过限制时,只要要用到D[1]+1枚硬币,剩余的硬币可以任意分配,所以方案数为 F[ S – (D[1]+1)C[1] ],当且仅当(S – (D[1]+1)C[1])>=0,否则方案数为0。其余情况类似,每次询问只用问16次,所以询问的时间复杂度为O(1)。

#include
#include
#include
#include
#define maxn 100010
using namespace std;
typedef long long ll;

ll ans, dp[maxn];

int c[4], d[4];

inline void read(int& num){
char ch = getchar();num = 0;
for(; ch < '!'; ch = getchar());
for(; ch > '!'; ch = getchar())
num = (num << 3) + (num << 1) + (ch ^ 48);
}

void solve(int num, int dep, ll s){
if(s < 0)return;
if(dep == 4){
if(num & 1)ans -= dp[s];
else ans += dp[s];
return;
}
solve(num + 1, dep + 1, s - (d[dep] + 1) * c[dep]);
solve(num, dep + 1, s);
}

int main(){
int test;
for(int i = 0; i < 4; i ++)
read(c[i]);

read(test);
dp[0] = 1;
for(int i = 0; i < 4; i ++)
for(int j = c[i]; j <= 100000; j ++)
dp[j] += dp[j - c[i]];
int x;
while(test --){
for(int i = 0; i < 4; i ++)
read(d[i]);
read(x);
ans = 0;
solve(0, 0, x);
printf("%lld\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  容斥原理 背包