(yysy) ,这个题真的是个不错的题目
Description
给定 (4) 种面值的硬币,然后有 (n) 次询问
每次给四个参数和一个面值,求每种硬币不使用超过参数个时凑成给定面值有多少方案
Solution
首先我们发现每次算方案显然是会飞的
然后我们可以先上来做一次完全背包,在每次询问的时候容斥
(就是(FlashHu)大佬的“询问重复利用信息”)
我们发现每次多出来的不合法方案在每种硬币使用超量的时候
所以我们就用状压 (+) 统计答案即可
说不清,具体看代码吧……
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
inline int read()
{
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
int c[5],d[5],s,T,f[100010];
signed main()
{
for(int i=1;i<=4;++i) c[i]=read(); T=read(); f[0]=1;
for(int i=1;i<=4;++i)
{
for(int j=c[i];j<=1e5;++j) f[j]+=f[j-c[i]];
}
while(T--)
{
for(int i=1;i<=4;++i) d[i]=read(); s=read();
int ans=f[s];
for(int i=1;i<=15;++i)
{
int del=0,t=s;
for(int j=1;j<=4;++j)
{
if(i&(1<<(j-1))) del++,t-=c[j]*(d[j]+1);
}
if(t<0) continue;
if(del&1) ans-=f[t];
else ans+=f[t];
}cout<<ans<<endl;
}
return 0;
}
}
signed main(){return yspm::main();}
Review
我们在思考题目的时候可以预处理已知信息来对付重复询问
另外:需要学习枚举子集的方法