bzoj 1042 HAOI2008 硬币购物

时间:2023-03-09 12:59:21
bzoj 1042 HAOI2008 硬币购物

这道题思路是在是神。

先dp出没有限制时候的方案数。

dp的时候注意 先循环 1..4 再循环 1..maxs 防止重复。边界是f[0] = 1。 这么基础的背包都忘记了=_=

接下来处理有重复的问题,容斥原理

    容斥原理说起来很简单,但有一些很神奇的应用,比如这道题。

最终的答案 = 没有限制的方案 - 其中一种超了限制的方案 + 其中两种超了限制的方案 - 三种超了限制的方案 + 四种超了限制的方案

ans = f[s] + f[s - c[i]*(d[i]+1)]  - ……  + f[s - c[1]*(d[1]+1)……]

为什么是 d[i]+1 呢?

至少用d[i]+1个,剩下的随意,又是不限制的方案数了。

上代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100010
using namespace std; int c[], n, d[], s;
long long f[N] = {}; void make_f()
{
f[] = ;
for (int j = ; j <= ; ++j)
for (int i = c[j]; i <= N-; ++i)
f[i] += f[i-c[j]];
} long long getans()
{
long long ans = f[s];
for (int i = ; i <= ; ++i)
if (s - c[i]*(d[i]+) >= )
ans -= f[s - c[i]*(d[i]+)];
for (int i = ; i <= ; ++i)
for (int j = i+; j <= ; ++j)
if (s - c[i]*(d[i]+) - c[j]*(d[j]+) >= )
ans += f[s - c[i]*(d[i]+) - c[j]*(d[j]+)];
for (int i = ; i <= ; ++i)
for (int j = i+; j <= ; ++j)
for (int k = j+; k <= ; ++k)
if (s - c[i]*(d[i]+) - c[j]*(d[j]+) - c[k]*(d[k]+) >= )
ans -= f[s - c[i]*(d[i]+) - c[j]*(d[j]+) - c[k]*(d[k]+)];
if (s - c[]*(d[]+) - c[]*(d[]+) - c[]*(d[]+) - c[]*(d[]+) >= )
ans += f[s - c[]*(d[]+) - c[]*(d[]+) - c[]*(d[]+) - c[]*(d[]+)];
return ans; } int main()
{
for (int i = ; i <= ; ++i) scanf("%d", &c[i]);
make_f(); scanf("%d", &n);
while (n--)
{
for (int i = ; i <= ; ++i) scanf("%d", &d[i]);
scanf("%d", &s);
printf("%I64d\n", getans());
}
return ;
}