题意
有四种货币, 它们的价值分别是 c[0], c[1], c[2], c[3] .
n 次询问, 每次给定 d[0], d[1], d[2], d[3], s, 问凑出 s , 且第 i 种货币不超过 c[i] 个的方案数.
c[i], d[i], s <= 100000 , n <= 1000 .
分析
设第 i 种货币取了 x[i] 个.
问题转化为求不定方程 c[0]x[0] + c[1]x[1] + c[2]x[2] + c[3]x[3] = s 的非负整数解的个数.
且满足 4 个限制条件 x[i] <= d[i] .
假如没有使用个数的限制, 我们可以通过背包 DP 进行预处理, f[s] 表示凑出 s 的方案数.
条件取并, 我们尝试用减法原理 + 容斥原理进行转化.
用 f[s] 减去满足一个 "x[i] > d[i]" 的限制条件的解的个数, 加上满足两个 "x[i] > d[i]" 的限制条件的解的个数, 减去满足三个 "x[i] > d[i]" 的限制条件的解的个数, 加上满足四个 "x[i] > d[i]" 的限制条件的解的个数.
问题转化为计算满足若干个 "x[i] > d[i]" 的限制条件的解的个数.
令 x[i] = y[i] + d[i] + 1 , 那么这一项的贡献为 c[i]x[i] = c[i]y[i] + c[i](d[i] + 1) , 令 s 减去 c[i](d[i]+1) , 那么就变成了没有限制使用个数的问题.
实现
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cctype> 5 #define F(i, a, b) for (register int i = (a); i <= (b); i++) 6 #define LL long long 7 inline int rd(void) { 8 int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == ‘-‘) f = -1; 9 int x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-‘0‘; return x*f; 10 } 11 12 const int S = 100000; 13 14 LL c[4], f[S+5]; 15 inline LL G(LL n) { return 0 <= n && n <= S ? f[n] : 0; } 16 17 int main(void) { 18 #ifndef ONLINE_JUDGE 19 freopen("bzoj1042.in", "r", stdin); 20 #endif 21 22 f[0] = 1; 23 F(i, 0, 3) { 24 c[i] = rd(); 25 F(j, c[i], S) f[j] += f[j - c[i]]; 26 } 27 28 int n = rd(); 29 F(t, 1, n) { 30 LL d0 = (rd()+1) * c[0], d1 = (rd()+1) * c[1], d2 = (rd()+1) * c[2], d3 = (rd()+1) * c[3], s = rd(); 31 LL res = G(s); 32 res = res - G(s - d0) - G(s - d1) - G(s - d2) - G(s - d3); 33 res = res + G(s - d0 - d1) + G(s - d0 - d2) + G(s - d0 - d3) + G(s - d1 - d2) + G(s - d1 - d3) + G(s - d2 - d3); 34 res = res - G(s - d0 - d1 - d2) - G(s - d0 - d1 - d3) - G(s - d0 - d2 - d3) - G(s - d1 - d2 - d3); 35 res = res + G(s - d0 - d1 - d2 - d3); 36 printf("%lld\n", res); 37 } 38 39 return 0; 40 }
时间: 2024-10-01 07:17:54