lucas

1.证明

2.用处

C(n, m) % p  =  C(n / p, m / p) * C(n%p, m%p) % p

大组合数求模

对于这种问题C(n, m) % p, n, m, p 都比较大的, 就要用卢卡斯定理了, 但是这里又分了两种情况, 一种是n, m 在1e6以内的, 并且case比较多的, 那么我们每次取调用卢卡斯肯定会T的, 所以我们要做的就是朴素的算就是了, 也就是直接调用组合数公式, 这个时候我们只要先预处理好1e6以内的带mod阶乘就行啦…. 类似于离线求

 1 const int maxn = 1e5+5;
 2 ll f[maxn];
 3 void init() { // 预处理阶乘
 4     f[0] = f[1] = 1;
 5     for (int i = 2 ; i <= 100001 ; i ++) {
 6         f[i] = i % mod * f[i-1] % mod;
 7     }
 8 
 9 }
10 ll qpow(ll x,ll y,ll mod) { // 快速幂
11     ll res = 1;
12     while(y){
13         if(y & 1) res = res * x % mod;
14         x = x * x % mod;
15         y >>= 1;
16     }
17     return res;
18 }
19 ll inv(ll n,ll mod) {  // 求逆元
20     return qpow(n,mod-2,mod);
21 }

但是如果n, m 非常大了, 也就是达到1e18的那种, 那么我们就要上卢卡斯定理了, 并且T一般会比较少(<100). 
同时如果mod数也非常的大, 那么在快速幂的同时还要套上快速乘才行, 防止爆….. 在线求.

 1 //求a * b % mod; 由于这道题的mod都很大,所以乘法都容易爆,故需要快速乘.
 2 ll mut_mod(ll a,ll b,ll mod)
 3 {
 4     ll res = 0;
 5     while(b){
 6         if(b&1) res = (res + a) % mod;
 7         a = (a + a) % mod;
 8         b >>= 1;
 9     }
10     return res;
11 }
12 //扩展GCD.
13 ll ex_gcd(ll a,ll b,ll &x,ll &y)
14 {
15     if(!b){
16         x = 1; y = 0;
17         return a;
18     }
19     ll r = ex_gcd(b,a%b,x,y);
20     ll tmp = x;
21     x = y ; y = tmp - a/b*y;
22     return r;
23 }
24 //快速幂用于求逆元,虽然ex_gcd也行,但是我不会用. xx.
25 ll qpow(ll x,ll y,ll mod)
26 {
27     ll res = 1;
28     while(y){
29         if(y & 1) res = mut_mod(res,x,mod) % mod;
30         x = mut_mod(x,x,mod) % mod;
31         y >>= 1;
32     }
33     return res;
34 }
35 //n % mod 的逆元
36 ll inv(ll n,ll mod)
37 {
38     return qpow(n,mod-2,mod);
39 }
40 //求n! % mod
41 ll fac(ll n,ll mod)
42 {
43     ll res = 1;
44     for(int i=2;i<=n;i++) res = res * i % mod;
45     return res;
46 }
47 //C(n, m) % mod
48 ll Comb(ll n,ll m, ll mod)
49 {
50     if(m>n) return 0;
51     return fac(n,mod) * inv(fac(m,mod),mod) % mod * inv(fac(n-m,mod),mod) % mod;
52 }
53 //卢卡斯定理,求C(n,m)%mod,且n,m,mod都很大.
54 ll Lucas(ll n, ll m,ll mod){
55     return m ? Lucas(n/mod, m/mod, mod) * Comb(n%mod, m%mod,mod) % mod : 1;
56 }
57 // 要求mod为质数.

此部分来自https://blog.csdn.net/Anxdada/article/details/79506800

3.题目

hdu5446

#include<cstdio>
typedef long long LL;
const int N = 100000 + 5;
LL mul(LL a, LL b, LL p){//快速乘,计算a*b%p 
    LL ret = 0;
    while(b){
        if(b & 1) ret = (ret + a) % p;
        a = (a + a) % p;
        b >>= 1;
    }
    return ret;
}
LL fact(int n, LL p){//n的阶乘求余p 
    LL ret = 1;
     for (int i = 1; i <= n ; i ++) ret = ret * i % p ;
    return ret ;
}
void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){
    if (!b) {d = a, x = 1, y = 0;}
    else{
        ex_gcd(b, a % b, y, x, d);
        y -= x * (a / b);
    }
}
LL inv(LL t, LL p){//如果不存在,返回-1 
    LL d, x, y;
    ex_gcd(t, p, x, y, d);
    return d == 1 ? (x % p + p) % p : -1;
}
LL comb(int n, int m, LL p){//C(n, m) % p
    if (m < 0 || m > n) return 0;
    return fact(n, p) * inv(fact(m, p), p) % p * inv(fact(n-m, p), p) % p;
}
LL Lucas(LL n, LL m, int p){
        return m ? Lucas(n/p, m/p, p) * comb(n%p, m%p, p) % p : 1;
}
LL china(int n, LL *a, LL *m){//中国剩余定理 
    LL M = 1, ret = 0;
    for(int i = 0; i < n; i ++) M *= m[i];
    for(int i = 0; i < n; i ++){
        LL w = M / m[i];
        //ret = (ret + w * inv(w, m[i]) * a[i]) % M;//这句写了会WA,用下面那句 
        ret = (ret + mul(w * inv(w, m[i]), a[i], M)) % M;
        //因为这里直接乘会爆long long ,所以我用快速乘(unsigned long long也是爆掉,除非用高精度)
    }
    return (ret + M) % M;
}
int main(){
    int T, k;
    LL n, m, p[15], r[15];
    scanf("%d", &T);
    while(T--){
        scanf("%I64d%I64d%d", &n, &m, &k);
        for(int i = 0; i < k; i ++){
            scanf("%I64d", &p[i]);
            r[i] = Lucas(n, m, p[i]);
        }
        printf("%I64d\n", china(k, r, p));
    }
}

 

posted @ 2018-07-18 19:25  谨川  阅读(136)  评论(0编辑  收藏  举报