hdu 5201 the mokey king (容斥原理)

题目大意:n个桃分给m只猴子,要求第1只猴子获得的桃子数目最多。问分配的方法数。


分析:

设第i个猴子分得的桃子数目为x[i](0<=x[i]<=n)

若没有要求,则问题转为求不定方程x[1]+x[2]+x[3]+……+x[m]=n,即x[2]+x[3]+……+x[m]=n-x[1] 的非负整数解的个数。

用插板法得到个数为C(n-x[1]+m-1-1,m-1-1)=C(n-x[1]+m-2,n-x[1])

现在要求第一个猴子得到的最多,可以枚举第1个猴子分得的桃子数目,考虑用总的方案数减去第一个猴子分得的不是最多的方案数。

根据容斥原理,

总方案数减去至少有1只猴子比第1个猴子分得的桃子多的方案数,加上至少有2个猴子比第1个猴子分得的桃子多……这样就可以得到结果。


接下来考虑至少有i只猴子比第1个分得的桃子多的方案数。

设当前枚举的第一个猴子分得的桃子数为x。

则转化为求不定x[2]+x[3]+……+x[m]=n-x至少有i个解大于x的解的个数。

任选其中i个x[],使得其大于x,方案数为C(m-1,i)。

此时等价于求x[2]+x[3]+…+x[m]=n-x-x*i的非负整数解个数,即C(n-x-x*i+m-2,n-x-x*i)个

于是至少有i只猴子比第1个分得的桃子多的方案数为C(m-1,i)*C(n-x-x*i+m-2,n-x-x*i)


大致思路就是这样。

另外此题还需注意以下几点:

1、求组合数需预处理,可通过费马小定理求逆元来求。

2、枚举第一个猴子分得的桃子个数可以从max(n/m,1)开始。


#include<stdio.h>
typedef long long LL;
#define mod 1000000007
LL pow(LL a, LL b) {
    LL s = 1;
    while(b) {
        if(b&1) s = (s * a) % mod;
        a = (a*a) % mod;
        b >>= 1;
    }
    return s;
}
LL f[200005],inv[200005];//注意数组大小
void init()
{
    f[0]=f[1]=inv[0]=inv[1]=1;
    for(LL i=2;i<=200000;++i){
        f[i]=f[i-1]*i%mod;
        inv[i]=pow(f[i],mod-2);
    }
}
LL C(int n,int m)
{
    if(m>n||m<0) return 0;
    return f[n]*inv[m]%mod*inv[n-m]%mod;
}
LL n,m;
LL cal(int x)
{
    LL ans=0;
    for(LL i=0,s=x;i<m&&s<=n;++i,s+=x)	//剪枝
    {
        LL t=C(m-1,i)*C(n-s+m-2,n-s)%mod;
        if(i&1) ans=((ans-t)%mod+mod)%mod;
        else ans=(ans+t)%mod;
    }
    return ans;
}

int main() {
    int t;
    scanf("%d",&t);
    init();
    while(t--)
    {
        scanf("%lld%lld", &n,&m);
        if(m==1) puts("1");
        else
        {
            LL ans=0;
            for(LL i=1;i<=n;++i) ans=(ans+cal(i))%mod;
            printf("%lld\n",ans);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值