HDU 5201 The Monkey King (容斥+数学组合+隔板法)

The Monkey King
Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 365 Accepted Submission(s): 115
Problem Description
As everyone known, The Monkey King is Son Goku. He and his offspring live in Mountain of Flowers and Fruits. One day, his sons get n peaches. And there are m monkeys (including GoKu), they are numbered from 1 to m, GoKu’s number is 1. GoKu wants to distribute these peaches to themselves. Since GoKu is the King, so he must get the most peach. GoKu wants to know how many different ways he can distribute these peaches. For example n=2, m=3, there is only one way to distribute these peach: 2 0 0.
When given n and m, you are expected to calculate how many different ways GoKu can distribute these peaches. Answer may be very large, output the answer modular 1000000007 instead.

Input
There are multiple test cases. In the first line of the input file there is an integer T indicates the number of test cases.
In the next T lines, each line contains n and m which is mentioned above.
[Technical Specification]
All input items are integers.

1≤T≤25

1≤n,m≤100000

Output
For each case,the output should occupies exactly one line.
See the sample for more details.

Sample Input
2
2 2
3 5

Sample Output
1
5

Hint
For the second case, there are five ways. They are
2 1 0 0 0
2 0 1 0 0
2 0 0 1 0
2 0 0 0 1
3 0 0 0 0

Source
BestCoder Round #36 ($)

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

题目大意:m个猴子分n个桃,要求第一个猴子的桃数严格大于其他猴子,问有多少种分法

题目分析:如果不考虑,限制条件,m个猴子分n个桃,且每个猴子可能分不到桃,则问题转化为部不定方程非负整数解的个数问题,设每个猴子得到的桃子为ai,则:
a1+a2+…+am <= n,(ai >= 0且ai为整数),用隔板法可以算出答案为C(n + m - 1, m - 1),现在考虑第一只猴子的桃数严格最大的条件,因为至少有k个猴子的桃数大于等于第一个猴子的情况很好算,即假设给了第一支猴子x个桃,先C(m-1, k),从剩下的m-1个猴子里选出k个猴子,每个猴子都给x个桃,那么还剩n - (k + 1) * x个桃,把这些桃再分给除了第一个猴子的剩下m-1个猴子的方案数还是之前那个公式,最后容斥一下,第一只猴子桃数最大的方案=总方案-至少一个猴子比第一个猴子多的方案数+至少二个猴子比第一个猴子多的方案数-…,典型容斥,注意判断组合数的边界,还有这题组合数很大,可以用费马小定理预处理组合数公式里分母上阶乘的逆元,来求组合数

如果元素互不相同组别相同,用组合数公式即可,但是元素相同,组别不同,就要用隔板法,这里的桃子是一样的,所以用隔板法分给不同的猴子。

坑点1:传入快速幂里面的a得用 long long
坑点2:因为用到了n+m-1,所以组合数的大小起码是数据范围的两倍
三:容斥原理 就是让i=0,开始,i=0代表的是全部数

#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
const int N = 2e5+100;
typedef long long ll;
ll f[N+100];
ll inv[N+100];
int n,m;
ll qpow(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1) res*=a,res%=mod;
        a*=a;
        a%=mod;
        b>>=1;
    }
    return res;
}



void init()
{
    f[0]=1;
    for(int i=1;i<=N;i++)
    {
        f[i]=f[i-1]*i%mod;
        f[i]%=mod;
    }
    inv[N]=qpow(f[N],mod-2);
    for(int i=N-1;i>=0;i--)
    {
        inv[i]=inv[i+1]*(i+1)%mod;
        inv[i]%=mod;
    }
}

ll C(int a,int b)
{
    return f[a]*inv[b]%mod*inv[a-b]%mod;
}

ll fen(int a,int b){
    return C(a+b-1,b-1);
}

ll cal(int x)
{
    if(x==n) return 1;
    ll i=0;
    ll res=0;
    while((n-(i+1)*x)>=0)
    {
        if(i>m-1) break;
        ll rr1=C(m-1,i)*fen(n-(i+1)*x,m-1);
        if(i&1)
            res=res-rr1,res%=mod;
        else res=res+rr1,res%=mod;
        i++;
    }
    return res;
}






int main()
{
    int t;
    init();
    while(scanf("%d",&t)!=EOF)
    {
        while(t--)
        {
            scanf("%d%d",&n,&m);
            if(m==1){
                printf("1\n");
                continue;
            }
            ll res=0;
            for(int i=1;i<=n;i++)
            {
                res=(res+cal(i)%mod)%mod;
            }
            printf("%lld\n",res );
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值