您的位置:首页 > 其它

BZOJ1042: [HAOI2008]硬币购物

2016-12-18 10:05 357 查看

Description

  硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s

i的价值的东西。请问每次有多少种付款方法。

Input

  第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000

Output

  每次的方法数

Sample Input

1 2 5 10 2

3 2 3 1 10

1000 2 2 2 900

Sample Output

4

27

HINT

Source

dp+容斥
预处理dp[i]表示i元钱的方案数(不考虑数量限制)
对于每次询问 答案为dp[S]-第一种超过限制-第二种超过限制-...+第一、二种同时超过限制...+四种同时超过限制
那么怎么求第i种超过限制呢
我们先钦定它用了d[i]+1个 然后总数减掉c[i]*(d[i]+1) 剩下的怎么选都会超过限制了
预处理O(s),每次询问O(16) 数据范围怎么这么小啊
#include <bits/stdc++.h>

using namespace std;

const int maxn = 100000;

int T, c[5] ,d[5], s;

long long ans, dp[maxn + 5];

inline void init()
{
dp[ 0 ] = 1 ;
for( int i = 1 ; i <= 4 ; i++ )
for( int j = c[ i ] ; j <= maxn ; j++ )
dp[ j ] += dp[ j - c[ i ] ];
}

void dfs(int now, int tot, bool flag)
{
if( tot < 0 ) return ;
if( now == 5 )
{
if( flag ) ans -= dp[ tot ];
else ans += dp[ tot ];
return ;
}
dfs( now + 1, tot - ( d[ now ] + 1 ) * c[ now ] , flag ^ 1 );
dfs( now + 1, tot, flag );
}

int main()
{
for( int i = 1 ; i <= 4 ; i++ ) scanf( "%d", &c[ i ] );
scanf( "%d", &T );
init();
while( T-- )
{
ans = 0;
for( int i = 1 ; i <= 4 ; i++ ) scanf( "%d", &d[ i ] );
scanf( "%d", &s );
dfs( 1, s, 0 );
printf( "%lld\n", ans );
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: