[模板] 常用数论 -> gcd、exgcd、乘法逆元、快速幂、快速乘、筛素数、快速求逆元、组合数

1.gcd
ll gcd(ll a, ll b)
{
    return b ? gcd(b, a % b) : a;
}
 

2.扩展gcd )extend great common divisor
 
ll exgcd(ll l, ll r, ll &x, ll &y)
{
    if(r == 0)
    {
        x = 1, y = 0;
        return l;
    }
    else
    {
        ll d = exgcd(r, l % r, y, x);
        y -= l / r * x;
        return d;
    }
}
 

3.求a关于m的乘法逆元
 
ll mod_inverse(ll a,ll m)
{
    ll x, y;
    if(exgcd(a, m, x, y) == 1)  //ax+my=1
        return (x % m + m) % m;
    return -1;  //不存在
}
 

补充:求逆元还可以用
ans=abmodm=(amod(m⋅b))/b
4.快速幂quick power
 
ll qpow(ll a, ll b, ll m)
{
    ll ans = 1;
    ll k = a % mod;
    while(b)
    {
        if(b & 1)
            ans = ans * k % m;
        k = k * k % m;
        b >>= 1;
    }
    return ans;
}
 

5.快速乘,直接乘会爆ll时需要它,也叫二分乘法。
 
ll qmul(ll a, ll b, ll m)
{
    ll ans = 0;
    ll k = a;
    ll f = 1;//f是用来存负号的
    if(k < 0)
    {
        f = -1;
        k = -k;
    }
    if(b < 0)
    {
        f *= -1;
        b = -b;
    }
    while(b)
    {
        if(b & 1)
            ans = (ans + k) % m;
        k = (k + k) % m;
        b >>= 1;
    }
    return ans * f;
}
 
6.中国剩余定理CRT (x=ai mod mi)
 
ll china(ll n, ll *a,ll *m) {
    ll M=1,y,x=0,d;
    for(ll i = 1; i <= n; i++) M *= m[i];
    for(ll i = 1; i <= n; i++) {
        ll w = M /m[i];
        exgcd(m[i], w, d, y);//m[i]*d+w*y=1
        x = (x + y*w*a[i]) % M;
    }
    return (x+M)%M;
}
 
7.筛素数,全局:int cnt,prime[N],p[N];
 
void isprime()
{
    cnt = 0;
    memset(prime,true,sizeof(prime));
    for(int i=2; i<N; i++)
    {
        if(prime[i])
        {
            p[cnt++] = i;
            for(int j=i+i; j<N; j+=i)
                prime[j] = false;
        }
    }
}
 
 

8.快速计算逆元
补充:>>关于快速算逆元的递推式的证明<< 

 
void inverse(){
    inv[1] = 1;
    for(int i=2;i<N;i++)
    {
        if(i >= M) break;
        inv[i] = (M-M/i)*inv[M%i]%M;
    }
}
 
 

9.组合数取模
n和m 10^5时,预处理出逆元和阶乘

 
ll fac[N]={1,1},inv[N]={1,1},f[N]={1,1};
ll C(ll a,ll b){
    if(b>a)return 0;
    return fac[a]*inv[b]%M*inv[a-b]%M;
}
void init(){//快速计算阶乘的逆元
    for(int i=2;i<N;i++){
        fac[i]=fac[i-1]*i%M;
        f[i]=(M-M/i)*f[M%i]%M;
        inv[i]=inv[i-1]*f[i]%M;
    }
}
 
 

n较大10^9,但是m较小10^5时,

 
ll C(ll n,ll m){
    if(m>n)return 0;
    ll ans=1;
    for(int i=1;i<=m;i++)
        ans=ans*(n-i+1)%M*qpow(i,M-2,M)%M;
    return ans;
}
 
 

n和m特别大10^18时但是p较小10^5时用lucas

10.Lucas大组合取模 

#define N 100005
#define M 100007
ll n,m,fac[N]={1};
ll C(ll n,ll m){
    if(m>n)return 0;
    return fac[n]*qpow(fac[m],M-2,M)%M*qpow(fac[n-m],M-2,M)%M;//费马小定理求逆元
}
ll lucas(ll n,ll m){
    if(!m)return 1;
    return(C(n%M,m%M)*lucas(n/M,m/M))%M;
}
void init(){
    for(int i=1;i<=M;i++)
        fac[i]=fac[i-1]*i%M;
}

 PY版CRT

def gcd(a, b):
    if(b == 0):
        return a
    else:
        return gcd(b, a % b)
 
def ex_gcd(dividend, divisor):
    if 0 == divisor:
        return 1, 0, dividend
    x2, y2, remainder = ex_gcd(divisor, dividend % divisor)
    temp = x2
    x1 = y2
    y1 = temp - int(dividend // divisor) * y2
    return x1, y1, remainder
 
rs1, rs2 = input().split()
n = int(rs1)
mx = int(rs2)
 
ai = []
bi = []
 
def ex_crt():
    M = bi[0]
    ans = ai[0]
    for i in range(1, n):
        a = M
        b = bi[i]
        c = (ai[i] - ans % b + b) % b
        g = gcd(a, b)
        x = ex_gcd(a, b)[0]
        bg = b // g
        if (c % g != 0):
            return -1
 
        x = (x * (c // g) % bg)
        ans = ans + x * M
        M = M * bg
        ans = (ans % M + M) % M
 
    ans = (ans % M + M) % M;
    return ans
 
 
for i in range(n):
    s1, s2 = input().split()
    bi.append(int(s1))
    ai.append(int(s2))
 
res = ex_crt()
 
if(res == -1):
    print("he was definitely lying")
     
elif(res <= mx):
    print(res)
     
else:
    print("he was probably lying")
原文地址:https://www.cnblogs.com/zeolim/p/12270419.html