msc的背包(挡板法)

链接:https://ac.nowcoder.com/acm/contest/217/D
来源:牛客网
 

题目描述

msc是个可爱的小女生,她喜欢许许多多稀奇古怪的小玩意。
一天,msc得到了一个大小为k的背包,她决定去买东西。
商店里有n种大小为1的物品和m种大小为2的物品。
由于msc希望买的东西尽量多,所以msc不希望买完东西之后背包还有空位(即买的所有东西的体积和必须等于k)。
她想知道自己有多少种购买物品的方案。
两种方案不同当且仅当存在一种物品在两种方案中的数量不同。

输入描述:

一行三个整数n,m,k,分别表示大小为1的物品的种类数和大小为2的物品的种类数以及背包的容量。

输出描述:

一行一个整数表示答案,由于答案可能很大,所以请对998244353取模后输出。

 

示例1

输入

复制

4 5 454

输出

复制

605690007

思路:我们考虑体积为1的物品,对每一种体积为1的物品要么拿奇数件,要么拿偶数件。如果我知道哪些体积为1的物品拿偶数件,那样我们就能把两件体积为1的物品捏成一件体积为2的物品,这样就能和体积为2的物品合起来,然后用挡板法计算啦。

其实这个问题如果用生成函数来表示的话,它的生成函数可以表示为1/ (1-x)^a  *  1/(1-x*x)^b

然后上下同乘以(1+x)^a  得到   (1+x)^a * [ 1 / (1-x*x)^(a+b)],这样就能枚举前面的次方数,后面用挡板法。

那么这个题的复杂度与a相关。即本题中枚举体积为1的物品奇数件的种类数,两种方法从两个角度说明了这个相同的问题。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6+777;
const ll mod = 998244353LL;

ll fac[maxn],dev[maxn];
ll C(ll n, ll m)
{
    return fac[n]*dev[m]%mod*dev[n-m]%mod;
}
ll qm(ll a, ll n)
{
    ll ans = 1;
    while(n)
    {
         if(n%2) ans = ans*a%mod;
         a = a*a%mod;
         n/=2LL;
    }
    return ans;
}
int main()
{
    for(ll i = 0; i <= 2000000; i++) fac[i] = i == 0 ? 1LL : fac[i-1]*i%mod;
    for(ll i = 2000000; i >= 0; i--) dev[i] = i == 2000000 ? qm(fac[i],mod-2) : dev[i+1]*(i+1)%mod;
    ll a,b,k,num1,num2;
    scanf("%lld%lld%lld",&a,&b,&k);
    ll ans = 0;
    ll tmp = 1;
    ll cur = a+b+k/2LL-1,curm = k/2LL;
    for(ll i = curm+1LL; i <= cur; i++) tmp = tmp*(i)%mod;
    tmp *= dev[a+b-1];
    tmp %= mod;
    for(ll i = 0; i <= a; i++)
    {
        if((k-i)%2 == 0)
        {
            ans += C(a,i)*tmp%mod;
            ans %= mod;
            tmp = tmp*curm%mod*qm(cur,mod-2)%mod;
            cur--;curm--;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值