lucas定理

lucas定理

快速求组合数取模

注:mod是少于1e5的质数

核心代码

//C(n, m) % mod
LL lucas(LL n, LL m, LL mod) {
    if(m == 0) return 1LL;
    return (C(n % mod, m % mod, mod) * lucas(n / mod, m / mod, mod)) % mod;
}
//关于C(n % mod, m % mod) % mod,可以这样求
//第一种
LL C(LL n, LL m, LL mod) {
   	LL inv[maxn];
   	inv[1] = 1;
    LL f = 1;
    for(int i = n - m + 1; i <= n; i++) f = f * i % mod;//(n-m+1)*(n-m+2)*...*n
    for(int i = 2; i <= m; i++) inv[i] = (mod - mod / i) * 1LL * inv[mod % i] % mod;//打表1~m关于mod的逆元
    for(int i = 2; i <= m; i++) inv[i] = inv[i] * inv[i-1] % mod;//m!关于mod的逆元
    return (f * inv[m]) % mod;
}
//第二种
LL power(LL a, LL b, LL p) {
    LL ret = 1;
    while(b) {
        if(b & 1) ret = ret * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return ret;
}
LL f[100010];//f[i]表示 i! % p
LL C(LL n, LL m, LL p) {
    if(n < m) return 0;
    return (f[n] * power(f[m], p-2, p)) % p * power(f[n-m], p-2, p) % p;
}

P3807 【模板】卢卡斯定理

#include <cstdio>
typedef long long LL;
LL C(LL n, LL m, LL p) {
    LL inv[100010];
    inv[1] = 1;
    LL f = 1;
    for(int i = n - m + 1; i <= n; i++) f = f * i % p;
    for(int i = 2; i <= m; i++) inv[i] = (p - p / i) * 1LL * inv[p % i] % p;
    for(int i = 2; i <= m; i++) inv[i] = inv[i] * inv[i-1] % p;
    return (f * inv[m]) % p;
}
LL lucas(LL n, LL m, LL p) {
    if(m == 0) return 1LL;
    return (C(n % p, m % p, p) * lucas(n / p, m / p, p)) % p;
}
int main() {
    int t;
    scanf("%d", &t);
    while(t--) {
        LL n, m, p;
        scanf("%lld %lld %lld", &m, &n, &p);
        n += m;
        printf("%lld\n", lucas(n, m, p));
    }
    return 0;
}

牛牛与组合数学

#include <cstdio>
#include <cstring>
typedef long long LL;
LL inv[150000];
LL mod[10] = {88897 ,58787 ,125453 , 7853 , 78569 ,145267 ,25867 ,105899 ,8803,4591};
LL C(LL n, LL m, LL p) {
    if(n < m) return 0;
    LL f = 1;
    inv[1] = 1;
    for(int i = n-m+1; i <= n; i++) f = f * i % p;
    for(int i = 2; i <= m; i++) inv[i] = (p - p / i) * 1LL * inv[p % i] % p;
    for(int i = 2; i <= m; i++) inv[i] = inv[i] * inv[i-1] % p;
    return ((f * inv[m]) % p);
}
LL lucas(LL n, LL m, LL p) {
    if(m == 0) return 1;
    return (C(n % p, m % p, p) * lucas(n / p, m / p, p)) % p;
}
char s[4000010];
int main() {
    LL n, m;
    scanf("%lld %lld %s", &n, &m, s);
    int flag = 0;
    int ns = strlen(s);
    for(int j = 0; j < 10; j++) {
        LL num = 0;
        for(int i = 0; i < ns; i++) {
            num = num * 10 + (s[i] - '0');
            num = num % mod[j];
        }
        LL num2 = lucas(n, m, mod[j]);
        if(num != num2) {
            flag = 1;
            printf("No!\n");
            break;
        }
    }
    if(flag == 0) printf("Yes!\n");
    return 0;
}

Saving Beans HUD 3037

松鼠有不多于m颗松果(0~m),他决定放到n棵树下(有些树可以不放),问有多少种方案。结果取模p

根据插板法,\(\sum_{i=0}^mC_{n+m-1}^{n-1}\) = \(C_{n-1}^{n-1}\) + \(C_n^{n-1}\) +...+ \(C_{n+m-1}^{n-1}\)

= \(C_n^n\) + \(C_n^{n-1}\) +...+ \(C_{n+m-1}^{n-1}\)

= \(C_{n+1}^n\) + \(C_{n+1}^{n-1}\) +... + \(C_{n+m-1}^{n-1}\)

=\(C_{n+m}^n\)

(组合数推导公式:\(C_n^m\) = \(C_{n-1}^m\) + \(C_{n-1}^{m-1}\))

这里用上面第一种求C(LL a, LL b, LL p)会wa,我搞不懂QAQ

#include <cstdio>
typedef long long LL;
LL power(LL a, LL b, LL p) {
    LL ret = 1;
    while(b) {
        if(b & 1) ret = ret * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return ret;
}
LL f[100010];
LL C(LL n, LL m, LL p) {
    if(n < m) return 0;
    return (f[n] * power(f[m], p-2, p)) % p * power(f[n-m], p-2, p) % p;
}
LL lucas(LL n, LL m, LL p) {
    if(m == 0) return 1LL;
    return (C(n % p, m % p, p) * lucas(n / p, m / p, p)) % p;
}
int main() {
    int t;
    scanf("%d", &t);
    while(t--) {
        LL n, m, p;
        scanf("%lld %lld %lld", &n, &m, &p);
        n += m;
        f[0] = f[1] = 1;
        for(int i = 2; i < p; i++) f[i] = f[i-1] * i % p;
        printf("%lld\n", lucas(n, m, p));
    }
    return 0;
}

posted @ 2020-04-04 16:53  小饭hhh  阅读(134)  评论(0编辑  收藏  举报