HDU 3037 Saving Beans (隔板法 Lucas定理 费马小定理 乘法逆元)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Tc_To_Top/article/details/83079461

Saving Beans

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 7780    Accepted Submission(s): 3135

 

Problem Description

Although winter is far away, squirrels have to work day and night to save beans. They need plenty of food to get through those long cold days. After some time the squirrel family thinks that they have to solve a problem. They suppose that they will save beans in n different trees. However, since the food is not sufficient nowadays, they will get no more than m beans. They want to know that how many ways there are to save no more than m beans (they are the same) in n trees.

Now they turn to you for help, you should give them the answer. The result may be extremely huge; you should output the result modulo p, because squirrels can’t recognize large numbers.

Input

The first line contains one integer T, means the number of cases.

Then followed T lines, each line contains three integers n, m, p, means that squirrels will save no more than m same beans in n different trees, 1 <= n, m <= 1000000000, 1 < p < 100000 and p is guaranteed to be a prime.

Output

You should output the answer modulo p.

Sample Input

2

1 2 5

2 1 5

Sample Output

3

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

题目大意:有n个盒子,最多有m个球,求可能的放球方案数对p取模,p保证是质数

题目分析:首先几个概念

1)隔板法:

        a)盒子不允许为空:m个球放成一条线,则存在m-1个空隙,用n-1个隔板将其分成n份,方案数为C[m-1][n-1]

        b)盒子允许为空:将a)中每个盒子各取出一个球,则问题转换成1),方案数为C[n+m-1][n-1]

2)Lucas定理:

        在n和m很大,p相对比较小且为质数时,可以使用Lucas定理求组合数

        公式:lucas(n, m) = (C(n % p, m % p) * lucas(n / p, m / p)) % p

3)费马小定理:

        若gcd(a, p) = 1,则a^(p-1)≡1 mod p

4)乘法逆元:

        若a*b≡1 mod p,则a和b互为乘法逆元,记为inv[a]=b,inv[b]=a,根据费马小定理:a*a^(p-2)≡1 mod p,因此一个数x关于1模p的乘法逆元即为x^(p-2)

5)除法取模

        (a/b)%p = (a*inv[b])%p=(a*b^(p-2))%p

回到题目,若恰有m个球,根据隔板法b)情况,可得公式C[n+m-1][n-1],最多m个球时则答案为(0<=i<=m)∑C[n+i-1][n-1] =>(0<=i<=m)∑C[n+i-1][i]=C[n-1][0]+C[n][1]+C[n+1][2]+...+C[n+m-1][m]

根据组合数公式C[n][m]=C[n-1][m]+C[n-1][m-1],因为C[n-1][0]=C[n][0],则可以得到C[n][0]+C[n][1]=C[n+1][1],

C[n+1][1]+C[n+1][2]=C[n+2][2],...,C[n+m-1][m-1]+C[n+m-1][m]=C[n+m][m],故最后答案为C[n+m][m]

#include <cstdio>
#define ll long long
using namespace std;
int n, m, p;

ll qpow(ll x, int n) {
    ll ans = 1;
    while (n) {
        if (n & 1) {
            ans = (ans * x) % p;
        }
        x = (x * x) % p;
        n >>= 1;
    }
    return ans;
}

ll C(int n, int m) {
    if (n < m) {
        return 0;
    }
    if (m == 0) {
        return 1;
    }
    if (n - m < m) {
        m = n - m;
    }
    ll a = 1, b = 1;
    while (m) {
        a = (a * n) % p;
        b = (b * m) % p;
        n--;
        m--;
    }
    return (a * qpow(b, p - 2)) % p;
}

ll lucas(int n, int m) {
    if (m == 0) {
        return 1;
    }
    return (C(n % p, m % p) * lucas(n / p, m / p)) % p;
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d %d %d", &n, &m, &p);
        printf("%d\n", lucas(n + m, m));
    }
}

猜你喜欢

转载自blog.csdn.net/Tc_To_Top/article/details/83079461
今日推荐