首页 > 其他 > 详细

Saving Beans HDU3037(组合数公式+Lucas定理)

时间:2020-02-06 19:27:20      阅读:88      评论:0      收藏:0      [点我收藏+]

题意:

将至多m个豆子(即0-m都可以)放到n棵树上(一棵树可以一颗也不放),问有多少种放的方法。

答案对p取模,n,m<=1e10,p<=1e5,保证p为素数

思路:

用隔板法可以得到,将m颗豆子放到n棵树上的方案数为C(n+m-1,n-1)=C(n+m-1,m)。则答案为\(\sum C(n+i-1,i)\)

利用组合数公式化简为C(n+m,m)

现在问题就是n和m很大, 线性时间无法求出阶乘。注意到p很小,因此可以利用lucas定理进行加速组合数的求解。

Lucas定理

\(C(s*p+q,t*p+r)\equiv C(s,t)*C(q,r) \pmod p\)

(C(n,m)当m大于n时组合数为0?)

即C(n,m)可以转换为$C(n/p,m/p)*C(n $% \(p,m\) % $p) $,后者是一个很小的组合数,可以通过预处理的阶乘来求解,而前者在以指数速度减小,因此可以递归求解,时间复杂度为O(log n)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
ll fact[maxn];
ll mod;
ll fp(ll b, ll x) {
    ll ans = 1;
    while (x) {
        if (x & 1) {
            ans = ans * b%mod;
        }
        b = b * b%mod;
        x >>= 1;
    }
    return ans;
}
ll inv(ll x) {
    return fp(x, mod - 2);
}
void init() {
    fact[0] = 1;
    for (int i = 1; i <= mod; i++) {
        fact[i] = fact[i - 1] * i %mod;
    }
}
ll lucas(ll n, ll m) {
    ll ans = 1;
    while (n&&m) {
        ll n1 = n % mod;
        ll m1 = m % mod;
        if (n1 - m1 < 0) {
            return 0;
        }
        ans = ans * fact[n1] % mod * inv(fact[n1 - m1] * fact[m1] % mod) % mod;
        n /= mod;
        m /= mod;
    }
    return ans;
}
int main() {
    int T;
    cin >> T;
    while (T--) {
        ll n, m;
        scanf("%lld%lld%lld", &n, &m, &mod);
        init();
        ll ans = lucas(n + m, m);
        printf("%lld\n", ans);
    }
}

Saving Beans HDU3037(组合数公式+Lucas定理)

原文:https://www.cnblogs.com/ucprer/p/12269667.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!