hdu-6397(组合数学+抽屉原理)

博客探讨了如何解决HDU-6397问题,即在限定取值范围内求数字组合的方案数。通过转化问题,使用组合数学中的隔板法,计算出无限制情况下的方案数。接着,博主分析了当数值可能超过限制时,如何利用容斥原理调整方案数,避免重复计数。整个解题过程涉及了组合计数和奇偶项的交替修正策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6397

题意:共有m个取值范围为[0,n-1]的数字,求使得总和为k的方案数。

思路:链接:https://blog.csdn.net/yu121380/article/details/82802502

我们可以把k看成k个1,通过m-1个隔板来分割成m个数字。但是这样做会有问题,就是数字可能为0,但是隔板法不允许这种情况存在,所以我们可以做一个等价处理,即将取值范围+1,即[1,n],那么相应的总和也要加上m,即k+m,则问题转化为 “共有m个取值范围为[1,n]的数字,求使得总和为m+k的方案数”, 根据隔板法可以得出无限制的情况下方案数为 C(m+k-1, m-1)。

对于此题,共有ans=C(k+m-1,m),但此时,会出现有的数>=n,所以我们要将减掉  出现1数值>=n,(有C(m,1)种选法)2个数值>=n(有C(m,2)种选法).....(k/n,m)个数值大于等于n的情况 ,f(i)=C(k+m-n*i-1,m)....但减去f(1)时,会多减了f(2),f(3)......,所以要加上f(2),但此时又多加了f(3)....如此往复,发现,当i为奇数时,减去f(i),i为偶数时,加上f(i) 

这道题的k,就相当于有k个小球,放到m个盒子里,如果不考虑Xi的约束条件,答案就是。我们考虑,这些答案里会有某些,我们应该把这些情况去掉。当有一个的时候,有种Xi。此时就相当于k个小球中,已经有n个小球放到了某个盒子里,接下来把剩下的k-n个小球放到m个盒子里就是有这么多种情况。所以我们用,这样的话,多减去了有两个的情况,我们又要加回来。考虑到这里,就不难发现,这可以用容斥原理解决。

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <map>
#include <set>
#include <bitset>
#include <stack>
#define ull unsigned long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define mems(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int N=2e5+10;
const double pi=acos(-1);
const int inf=0x3f3f3f3f;
const int M=50000+5;

ll n,m,k;
ll f[N],inv[N];
ll qpow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1)
            ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans%mod;
}

void init()
{
    f[0]=inv[0]=1;
    for(int i=1;i<N;i++)
    {
        f[i]=f[i-1]*i%mod;
        inv[i]=qpow(f[i],mod-2);
    }
}
ll CC(ll n,ll m)
{
    return f[n]*inv[n-m]%mod*inv[m]%mod;
}
int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%lld%lld",&n,&m,&k);
        ll ans=CC(k+m-1,m-1);
        ll len=min(k/n,m);
        for(int i=1;i<=len;i++)
        {
            if(i&1)
                ans=(ans-CC(m,i)*CC(k+m-1-i*n,m-1)%mod+mod)%mod;
            else
                ans=(ans+CC(m,i)*CC(k+m-1-i*n,m-1)%mod)%mod;
        }
        printf("%lld\n",ans%mod);
    }
}

   


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值