您的位置:首页 > 其它

hdu 5201 The Monkey King && BestCoder Round #36

2015-04-17 12:36 399 查看


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]
所有输入均为整数。
1≤T≤25
1≤n,m≤100000


输出描述
对于每一个数据在一行中输出答案。
查看样例可以获得更多信息。


输入样例
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);
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: