HDU 3037 Saving Beans 多重集合的结合 lucas定理

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

  题目描述: 要求求x1 + x2 + x3 + ...... + xn <= m 非负整数解的个数, 结果对P取模, 输入的变量是n, m, p, P一定是素数

  解题思路: x1 + ... + xn = m 非负整数解的个数是C(n+m-1, n) , 所以答案就是 C(n+0-1, 0) + C(n+1-1, 1) + ...... C(n+m-1, n) 对P取模,

         由于组合数公式C(n, m) = C(n-1, m-1) + C(n-1, m) 所以该答案两两合并得C(n+m, n) , 又因为n, m 都非常大, 所以想到Lucas定理, 自己还不知道Lucas的原理, 健完身回来看

  代码: 

#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iterator>
#include <cmath>
#include <algorithm>
#include <stack>
#include <deque>
#include <map>
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define mem0(a) memset(a,0,sizeof(a))
#define sca(x) scanf("%d",&x)
#define de printf("=======\n")
typedef long long ll;
using namespace std;

const int maxn = 100005;
ll n, m, p;
ll f[maxn];

void init( ll p ) {
    f[0] = 1;
    for( int i = 1; i <= p; i++ ) {
        f[i] = f[i-1] * i % p;
    }
}

ll q_power(ll a, ll b, ll p) {
    ll ret = 1;
    while( b ) {
        if( b & 1 ) ret = ret * a % p;
        b >>= 1;
        a = a * a % p;
    }
    return ret % p;
}

ll lucas( ll n, ll m, ll p ) {
    ll ans = 1;
    while( n && m ) {
        ll nn = n % p, mm = m % p;
        if( nn < mm ) return 0;
        ans = ans * f[nn] * q_power(f[mm]*f[nn-mm]%p, p-2, p) % p;
        n /= p;
        m /= p;
    }
    return ans;
}

int main() {
    int t;
    sca(t);
    while( t-- ) {
        scanf( "%I64d%I64d%I64d", &n, &m, &p );
        init(p);
        printf( "%I64d\n", lucas(n+m, n, p) );
    }
    return 0;
}
View Code

  思考: 自己一开始只做到了答案加和那一步, 以为答案是用到逆元呢, 结果一看n, m的取值范围傻眼了, 其实化简到那个式子应该能想到最终等式的.........没化简出来最终的等式是真的菜, 其实我感觉那个公式有点Dp 的意思噢.........lucas自己一直听说今天终于了解了一些了, 继续加油, 健身回来之后去了解Lucas的原理

posted on 2017-08-30 18:45  FriskyPuppy  阅读(143)  评论(0编辑  收藏  举报

导航