hdu 5201 The Monkey King && BestCoder Round #36

The Monkey King

 
 Accepts: 19
 
 Submissions: 71
 Time Limit: 8000/4000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
问题描述
就像大家所知道的,美猴王的名字叫孙悟空。他和他的后代们生活在花果山。一天,他的儿子得到了
   
   
    
    n
   
   个桃子。现在他们有
   
   
    
    m
   
   个猴子(包括悟空在内),他们被从
   
   
    
    1
   
   
   
   
    
    m
   
   标号,悟空的号码是
   
   
    
    1
   
   。悟空想把这些桃子分给他们自己。由于悟空是大王,所以他获得的桃子必须是最多的。悟空想知道有多少种不同的分配方法。
例如n=2,m=3的时候,只有一种分法2 0 0。
当给定
   
   
    
    n,m
   
   时,你的任务是计算悟空可以有多少种不一样的方法来分配这些桃子。由于答案比较大输出对
   
   
    
    1000000007
   
   取余的结果即可。
输入描述
多组测试数据。在输入文件的第一行有一个整数T,表示有T组数据。
在接下来的T行,每行包含n和m。他们的含义在上边已经提到。

[Technical Specification]
所有输入均为整数。

   
   
    
    1T25
   
   

   
   
    
    1n,m100000
   
   
输出描述
对于每一个数据在一行中输出答案。
查看样例可以获得更多信息。
输入样例
2
2 2
3 5
输出样例
1
5
Hint
第二组样例中有5种分配方案,他们是
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



题解:1.官方题解是用母函数写的,没有搞懂,私下偷看了别人的代码发现可以用容斥原理做

   2.目标是求x1 + x2 ..... xm = n && x1 > x2,....xm

   3.首先是忽略掉后面条件利用隔板法求出 x1 + x2 ..... xm = n有C(m - 1,n + m - 1)种情况

   4.推导方式:求x1 + x2 ..... xm = n有多少个非负整数解<==>x1 + x2 ..... xm = n + m有多少个正整数解,通

过在n + m个一之间插入m - 1个隔板可以得出ans = C(m - 1,n + m - 1)

   5.下面我们需要刨除那些不合法的情况,枚举x1的大小u,然后可以用到容斥原理,即是:ans - 有至少一个猴子

所拿桃子大于等于u的数量 + 有至少两个猴子所拿桃子大于等于u的数量......

  6.至于复杂度:大概是n + n / 2 + n / 3 + ..... + n / n = O(nln(n))


#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL;
#define MOD 1000000007
#define MAXN 200005
LL d[MAXN],nd[MAXN];
int n,m;
LL _pow(LL a,int b)
{
    LL ans = 1;
    while(b)
    {
        if(b & 1)ans = ans * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return ans;
}
LL C(int a,int b)
{
    return d[a] * nd[b] % MOD * nd[a - b] % MOD;
}
LL f(int a,int b)
{
    return C(a + b - 1,b - 1);
}
LL cal(int u)
{
    LL ans = 0;
    for(int i = 1;(i + 1) * u <= n && i <= m - 1;i++)
    {
        LL cur = C(m - 1,i) * f(n - (i + 1) * u,m - 1);
        if(i & 1)ans = ((ans - cur) % MOD + MOD) % MOD;
        else ans = (ans + cur) % MOD;
    }
    return ans;
}
void init()
{
    d[0] = d[1] = nd[0] = nd[1] = 1;
    for(int i = 2;i < MAXN;i++)
    {
        d[i] = d[i - 1] * i % MOD;
        nd[i] = _pow(d[i],MOD - 2);
    }
}
int main()
{
    int _;
    scanf("%d",&_);
    init();
    while(_--)
    {
        scanf("%d%d",&n,&m);
        LL ans = f(n,m);
        for(int u = 0;u <= n;u++)
            ans = (ans + cal(u)) % MOD;
        printf("%I64d\n",ans);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值