[BZOJ1042]HAOI2008硬币购物|DP|容斥原理

数学题太难了,哭瞎。。先预处理出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);
	}
}


 
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值