HDU 3944

    原來劍哥講組合數取模的時候說過Lucas定理,但是不是很明白。總之C(n,k)%p = C(n/p,k/p)*C(n%p,k%p)%p。這裏要注意如果k>n, C(n,k)=0。C(n,k)%p可以通過求逆元的方法計算。不過C(n,k)%p = n!/(k!*(n-k)!)%p = n!*((k!*(n-k)!)^(p-2))%p是更好的方法,編碼簡單一點,時間效率高一點。

    還要注意一點,這題比較噁心,每次都初始化階乘數組肯定會超時。考慮到10000以內只有1229個素數,所以可以先打表。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <memory.h>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <algorithm>
#include <iostream>
#include <sstream>

using namespace std;

// Lucas定理求解C(n,k)%p, p是素數
// C(n,k)%p = C(n%p,k%p)*C(n/p,k/p)%p
// C(n,k)%p = n!/(k!*(n-k)!)%p = n!*((k!*(n-k)!)^(p-2))%p
// pow_mod(a,b,p)求a^b%p的值, 快速冪取模算法
// com_mod(n,k,p)求C(n,k)%p的值
// lucas(n,k,p)使用lucas定理求C(n,k)%p的值

int pow_mod(int a, int b, int p) {
    int r = 1, t = a;
    while (b) {
        if (b & 1) r = r * t % p;
        t = t * t % p;
        b >>= 1;
    }
    return r;
}

int com_mod(int n, int k, int p, int *fac) {
    int a, b;
    if (k > n) return 0;
    a = fac[n];
    b = fac[n-k] * fac[k] % p;
    return a * pow_mod(b, p-2, p) % p;
}

int lucas(int n, int k, int p, int *fac) {
    int r = 1, a, b;
    while (n && k && r) {
        a = n % p;
        b = k % p;
        n /= p;
        k /= p;
        
        r = r * com_mod(a, b, p, fac) % p;
    }
    return r;
}

int pri[1250], cnt;
int fac[1250][10000];
bool not_prime[10005];

void init() {
    int i, j, p;
    cnt = 0;
    for (i = 2; i < 10000; i++) {
        if (not_prime[i]) continue;
        pri[cnt++] = i;
        for (j = i + i; j < 10000; j += i)
            not_prime[j] = 1;
    }
    for (i = 0; i < cnt; i++) {
        fac[i][0] = 1;
        p = pri[i];
        for (j = 1; j < p; j++)
            fac[i][j] = j * fac[i][j-1] % p;
    }
}

int *find(int p) {
    int l = 0, r = cnt - 1;
    while (l <= r) {
        int m = (l + r) / 2;
        if (pri[m] == p) return fac[m];
        else if (pri[m] < p) l = m + 1;
        else r = m - 1;
    }
    return fac[0];
}

int main() {
    int n, k, p, t = 1;
    init();
    while (scanf("%d %d %d", &n, &k, &p) != EOF) {
        if (k > n - k) k = n - k;
        printf("Case #%d: ", t++);
        printf("%d\n", (lucas(n+1, k, p, find(p)) + n - k) % p);
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章