您的位置:首页 > 其它

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

2015-04-17 10:37 465 查看
数学题太难了,哭瞎。。先预处理出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
;
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);
}
}



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: