数学题太难了,哭瞎。。先预处理出f[i]为不设限制时i可能的组成方法,F[i]=Sum{F[i-C[k]] | i-C[k]>=0 且 k=1..4},为避免方案重复,要以k为阶段递推,边界条件为F[0]=1,这样预处理的时间复杂度就是O(S)。接下来对于每次询问,奇妙的解法如下:根据容斥原理,答案为 得到面值S的超过限制的方案数 - 第1种硬币超过限制的方案数 - 第2种硬币超过限制的方案数 - 第3种硬币超过限制的方案数 - 第4种硬币超过限制的方案数 + 第1,2种硬币同时超过限制的方案数 + 第1,3种硬币同时超过限制的方案数 + ...... + 第1,2,3,4种硬币全部同时超过限制的方案数。当第1种硬币超过限制时,只要要用到D[1]+1枚硬币,剩余的硬币可以任意分配,所以方案数为 F[ S - (D[1]+1)C[1] ],当且仅当(S - (D[1]+1)C[1])>=0,否则方案数为0。其余情况类似,每次询问只用问16次,所以询问的时间复杂度为O(1)。By BYvoid。。容斥部分可以dfs搞定。
<pre name="code" class="cpp">#include<cstdio>
#include<iostream>
#define N 100005
#define ll long long
using namespace std;
int T,s,i,j,c[5],d[5];
ll ans,f[N];
void dfs(int x,int k,int sum)
{
if (sum<0) return;
if (x>4)
{
ans+=k*f[sum];
return;
}
dfs(x+1,k,sum);
dfs(x+1,-k,sum-(d[x]+1)*c[x]);
}
int main()
{
for (i=1;i<=4;i++) scanf("%d",&c[i]);
scanf("%d",&T);
f[0]=1;
for (j=1;j<=4;j++)
for (i=c[j];i<=100000;i++)
f[i]+=f[i-c[j]];
while (T--)
{
for (i=1;i<=4;i++) scanf("%d",&d[i]);
scanf("%d",&s);
ans=0ll;
dfs(1,1,s);
printf("%lld\n",ans);
}
}