洛谷传送门
BZOJ传送门
题目描述
硬币购物一共有 4 4 4种硬币。面值分别为 c 1 , c 2 , c 3 , c 4 c_1,c_2,c_3,c_4 c1,c2,c3,c4。某人去商店买东西,去了 t o t tot tot次。每次带 d i d_i di枚 c i c_i ci硬币,买 s i s_i si的价值的东西。请问每次有多少种付款方法。
输入输出格式
输入格式:
第一行 c 1 , c 2 , c 3 , c 4 , t o t c_1,c_2,c_3,c_4,tot c1,c2,c3,c4,tot 下面 t o t tot tot行 d 1 , d 2 , d 3 , d 4 , s d_1,d_2,d_3,d4,s d1,d2,d3,d4,s
输出格式:
每次的方法数
输入输出样例
输入样例#1:
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900
输出样例#1:
4
27
说明
d i , s ≤ 100000 d_i,s\le 100000 di,s≤100000
t o t < 1000 tot<1000 tot<1000
解题分析
先预处理出无限制的情况, 然后钦定先选 d i + 1 d_i+1 di+1枚, 剩下的所有方案都是不满足的, 减去即可。
这样搞会算重, 多减去既选 d i + 1 d_i+1 di+1枚 c i c_i ci和 d i + 1 + 1 d_{i+1}+1 di+1+1枚 c i + 1 c_{i+1} ci+1这类情况, 因此我们容斥掉即可。
代码如下:
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cctype>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 100500
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
ll sum[MX << 1], ans;
int c[5], tot, d[5], mon;
void DFS(R int now, R int typ, R int inc)
{
if(inc > mon) return;
if(now == 5) return ans += inc ? typ * sum[mon - inc] : 0, void();
DFS(now + 1, typ, inc);
DFS(now + 1, typ * -1, inc + c[now] * (d[now] + 1));
}
int main(void)
{
sum[0] = 1;
in(c[1]), in(c[2]), in(c[3]), in(c[4]), in(tot);
for (R int i = 0; i <= 100000; ++i) sum[i + c[1]] += sum[i];
for (R int i = 0; i <= 100000; ++i) sum[i + c[2]] += sum[i];
for (R int i = 0; i <= 100000; ++i) sum[i + c[3]] += sum[i];
for (R int i = 0; i <= 100000; ++i) sum[i + c[4]] += sum[i];
W (tot--)
{
in(d[1]), in(d[2]), in(d[3]), in(d[4]), in(mon);
ans = sum[mon];
DFS(1, 1, 0);
printf("%lld\n", ans);
}
}