hdu 5201 The Monkey King && BestCoder Round #36

来源:互联网 发布:数据安全保护制度 编辑:程序博客网 时间:2024/05/10 02:31

The Monkey King

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