[haoi2008]硬币购物
描述 Description
一共有4种硬币,面值分别为c1,c2,c3,c4. 阿Q带着一些硬币去商店买东西,他带了d1枚第一种硬币,d2枚第二种硬币,d3枚第三种硬币,d4枚第四种硬币,若想买一个价值为s的东西,问阿Q有多少种付coins的方法.
比如c={1,2,5,10},d={3,2,3,1},s=10,一共有4种方法:
10=1+1+1+2+5
10=1+2+2+5
10=5+5
10=10
注意,阿Q可能会去很多次商店,每次带的硬币数量和要买的东西价值可能不一样,你需要对每一次都求出方法总数。
输入格式 Input Format
输入第一行是5个正数c1,c2,c3,c4,tot,分别表示4种硬币的面值和阿Q去商店的次数,接下来tot行,每行5个非负整数,d1,d2,d3,d4,s.
输出格式 Output Format
输出有tot行,表示第i次付coins的方法总数,保证答案在int64/long long范围内.
样例输入 Sample Input
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900
样例输出 Sample Output
4
27
时间限制 Time Limitation
1s
注释 Hint
数据范围
100%的测试数据, d1,d2,d3,d4,s <=100000.
30%的测试数据, tot<=50.
100%的测试是数据, tot<=1000.
来源 Source
haoi2008
虽说haoi日常出一些**的题目,但这道题用来练一下容斥还是蛮不错的。一个值得思考一下的题。
因为s最大就为100000,最多也就4中硬币,所以你可以开一个数组把所有可以得到的总硬币面值的情况记录下来。然后利用容斥的思想用无限制的总数减去超出限制的数量就等于方案数。具体:先减去单个超过限制的方案,在加上两两超过限制的方案数,因为在你减去单个方案数的时候重复减了(容斥原理)然后挨着减就好了,最后再把4个超过限制加起来就好了
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; int c[5],t,d[5]; long long f[110000]; long long ans=0; int main() { for(int i=1;i<=4;i++) cin>>c[i]; cin>>t; f[0]=1; for(int i=1;i<=4;i++) for(int j=c[i];j<=100000;j++) f[j]+=f[j-c[i]]; for(int i=1;i<=t;i++) { for(int j=1;j<=4;j++) cin>>d[j]; int s; cin>>s; ans=f[s]; for(int j=1;j<=4;j++) { int h=s-c[j]*(d[j]+1); if(h>=0) ans-=f[h]; } for(int j=1;j<=4;j++) { for(int k=j+1;k<=4;k++) { int h=s-c[j]*(d[j]+1)-c[k]*(d[k]+1); if(h>=0) ans+=f[h]; } } int h=0; for(int j=1;j<=4;j++) { h=0; for(int k=1;k<=4;k++) if(j!=k) h+=(c[k]*(d[k]+1)); if(s-h>=0) ans-=f[s-h]; } int y=s-(h+c[4]*(d[4]+1)); if(y>=0) ans+=f[y]; cout<<ans<<endl; } return 0; }