數論-Lucas(盧卡斯定理)
阿新 • • 發佈:2019-02-20
Lucas定理是用來求 c(n,m) mod p,p為素數的值。
對於C(n, m) mod p。這裡的n,m,p(p為素數)都很大的情況。就不能再用C(n, m) = C(n - 1,m) + C(n - 1, m - 1)的公式遞推了。
應用:大組合數求模
表示式C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p求C(n, m) mod 10007
/*int Lucas (ll n , ll m , int p) { return m == 0 ? 1 : 1ll*comb (n%p , m%p , p) * Lucas (n/p,m/p,p)%p ; }*/ //comb()函式中,因為q , r < p , 所以這部分暴力完成即可。 //C++求C(n, m) mod 10007 版本二 要求p z在100000左右 ll f[N]; void init(int p) { //f[n] = n! f[0] = 1; for (int i=1; i<=p; ++i) f[i] = f[i-1] * i % p; } ll pow_mod(ll a, ll x, int p) { ll ret = 1; while (x) { if (x & 1) ret = ret * a % p; a = a * a % p; x >>= 1; } return ret; } ll Lucas(ll n, ll k, int p) //C (n, k) % p { ll ret = 1; while (n && k) { ll nn = n % p, kk = k % p; if (nn < kk) return 0; //inv (f[kk]) = f[kk] ^ (p - 2) % p ret = ret * f[nn] * pow_mod (f[kk] * f[nn-kk] % p, p - 2, p) % p; n /= p, k /= p; } return ret; } int main(void) { init (p); printf ("%I64d\n", Lucas (n, m, p)); return 0; }
也可以採用下面的方法,先把mod的階乘存起來:
void init(int mod){ //對mod取餘後,一定小於mod,因此把mod的階乘存起來就夠用 f[0] = 1; for(int i = 1 ; i <= mod ;i ++ ){ f[i] = f[i-1] * i % mod; } } void ex_gcd(LL a,LL b,LL& d,LL& x,LL& y) { if(!b) { d = a; x = 1; y = 0; } else{ ex_gcd(b, a%b, d, y, x); y -= x*(a/b); } } LL inv(LL a,LL m) //歐幾里得逆元,也可以費馬小定理 { LL d, x, y; ex_gcd(a, m, d, x, y); return d == 1 ? (x+m)%m : -1; } LL Lucas(LL m , LL n , LL p){ LL res = 1; while(n && m){ LL n1 = n % p ; LL m1 = m % p ; res = res * f[n1] * inv(f[n1-m1],p) * inv(f[m1],p)%p; n /= p; m /= p; } return ( res % p + p ) % p; }