标签:
和
,并且
是素数
这个问题有个叫做Lucas的定理,定理描述是,如果
那么得到
【卢卡斯(Lucas)定理】
Lucas定理用来求C(a,b)mod p的值,其中p为素数。
数学表达式为:
Lucas(a,b,q)=C(a%q,b%q)*Lucas(a/p,b/p,p);
Lucas(a,0,q)=0;
通过这个定理就可以很方便的把大数的组合转化成小数。但其中还是要求C(a%q,b%q)%p,所以这里引入逆元来求。
【定义】若整数a,b,p, 满足a·b≡1(mod p).则称a 为b 模p 的乘法逆元, 即a=b- 1mod p.其中, p 是模数。
应用到组合数中来就是:
a!/[b!*(a-b)!] % p == a! * [b!*(a-b)!]-1 %p
【逆元求法】:
对于正整数和
,如果有
,那么把这个同余方程中
的最小正整数解叫做
模
的逆元。
逆元一般用扩展欧几里得算法来求得,如果为素数,那么还可以根据费马小定理得到逆元为
。
应用费马小定理,ap-1=1 mod p ,即 a*ap-2=1 mod p
也就是说 ap-2就是a的逆元。
当然这里求出来的逆元是在取模p的逆元,对我们最终目标没有影响。这也是比较方便而且比较好的方法。
#include <cstdio> #include <iostream> #include <sstream> #include <cmath> #include <cstring> #include <cstdlib> #include <string> #include <vector> #include <map> #include <set> #include <queue> #include <stack> #include <algorithm> using namespace std; #define ll long long #define _cle(m, a) memset(m, a, sizeof(m)) #define repu(i, a, b) for(int i = a; i < b; i++) #define repd(i, a, b) for(int i = b; i >= a; i--) #define sfi(n) scanf("%d", &n) #define sfl(n) scanf("%I64d", &n) #define pfi(n) printf("%d\n", n) #define pfl(n) printf("%I64d\n", n) #define MAXN 1000005 ll quickpow(ll m, ll n , ll k){ ll ans = 1; while(n){ if(n & 1)//如果n是奇数 ans = (ans * m) % k; n = n >> 1;//位运算“右移1类似除2” m = (m * m) % k; } return ans; } //ll quickpow(ll a, ll b, ll p) //{ // ll ans = 1; // a %= p; // while(b) // { // if(b & 1) // { // ans = ans * a % p; // b--; // } // b >>= 1; // a = a * a % p; // } // return ans; //} ll C(ll n, ll m, ll p) { if(m > n) return 0; ll ans = 1; for(int i = 1; i <= m; i++) { ll a = (n - m + i) % p; ll b = i % p; ans = ans * (a * quickpow(b, p - 2, p) % p) % p; } return ans; } ll Lucas(ll n, ll m, ll p) { if(m == 0) return 1; else return (C(n % p, m % p, p) * Lucas(n / p, m / p, p)) % p; } int main() { int T; sfi(T); while(T--) { ll n, m, p; sfl(n), sfl(m), sfl(p); pfl(Lucas(n, m, p)); } return 0; }
标签:
原文地址:http://www.cnblogs.com/sunus/p/4722935.html