[模板] 数学基础:快速幂/乘/逆元/exGCD/(ex)CRT/(ex)Lucas定理

方便复制

快速乘/幂

时间复杂度 \(O(\log n)\).

ll nmod;
//快速乘
ll qmul(ll a,ll b){
    ll l=a*(b>>hb)%nmod*(1ll<<hb)%nmod;
    ll r=a*(b&((1<<hb)-1))%nmod;
    return (l+r)%nmod;
}
//快速幂
ll qpow(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1)res=res*a%nmod;
        a=a*a%nmod;
        b>>=1;
    }
    return res;
}

exgcd

内容

解不定方程 $ ax+by = c $

时间复杂度 \(O(\log n)\).

void exgcd(ll a,ll b,ll& x,ll& y,ll& d){ //a&b should > 0
    b==0?(x=1,y=0,d=a):(exgcd(b,a%b,y,x,d),y-=x*(a/b));
}
//use
    ll a,b,c;
    ll m,x,y;
    exgcd(a,b,x,y,m);
    if(c%m!=0)cout<<"No\n"; //无解
    else{
        c/=m,x*=c,y*=c,a/=m,b/=m;
//令x取最小非负整数
        x1=x%b;if(x1<0)x1+=abs(b);
        y1=(c-a*x1)/b;
//令y取最小非负整数
        y1=y%a;if(y1<0)y1+=abs(a);//(y>=0?y%a:y%a+abs(a));
        x1=(c-b*y1)/a;
    }

逆元

内容

\(n * x \equiv 1 (mod m)\) 最小正整数解.

单个数

时间复杂度 \(O(\log n)\).

//1:qp(n,nmod-2)
//2
ll getv(ll n){return n<0?n+nmod:n;}
ll inv(ll n){
    ll x,y,d;
    exgcd(getv(n%nmod),p,x,y,d);
    return x%p+(x<0?p:0);
}

线性求逆元

时间复杂度 \(O(n)\).

  1. 公式法(并不能记住板子)
  2. 阶乘法
    利用下面的公式:
    \[ (n!)^{-1} = ((n+1)!)^{-1} \cdot (n+1)\]

\[ n^{-1} = (n!)^{-1} \cdot (n-1)!\]

代码
ll fac[nsz],ifac[nsz];
void init(int bnd){
    fac[0]=1;
    rep(i,1,bnd)fac[i]=i*fac[i-1]%nmod;
    ifac[bnd]=inv(fac[bnd]);
    repdo(i,bnd-1,0){
        ifac[i]=ifac[i+1]*(i+1)%nmod;
    }
}

CRT

中国剩余定理 && 扩展中国剩余定理 - niiick - CSDN博客

内容

解线性同余方程组 \(x \equiv a_i \pmod{m_i}, \forall i \in \{1, 2, \cdots , n\}\). 其中\(m_i\)两两互质.

\(M=\prod_{i=1}^nm_i\), \(M_i=\frac M{m_i}\);
\(M_i^{-1}\)\(M_i\) 关于 \(\bmod m_i\)的逆元,

则可以构造出通解
\[x=kM+\sum_{i=1}^k a_iM_iM_i^{-1}, k \in \mathbb{N}\].

时间复杂度 \(O(n \cdot 逆元)\), 通常为 \(O(n \log n)\).

代码

ll crt(ll a,ll m,ll m0){//m0 | m; (m0,m/m0)=1
    return m/m0*inv(m/m0,m0)%m*a%m;
}

excrt

模数不互质.

利用合并的思想求解.

时间复杂度\(O(n \log n)\).

代码

ll excrt(ll *a,ll *m,ll n){
    ll a0=a[1],m0=m[1],x,y,g;
    rep(i,2,n){
        g=exgcd(m0,m[i],x,y);
        if((a[i]-a0)%g!=0)return -1;
        x=(a[i]-a0)/g*x%(m[i]/g);
        a0+=x*m0;
        m0=m0/g*m[i];
        a0%=m0; 
    }
    return a0<0?(a0%m0+m0):(a0%m0);
}

Lucas定理

Lucas定理 - permui - 博客园

内容

\(\binom n m \bmod p\), 保证\(p \in \{ prime \}\).

\(n=(a_0a_1\dots a_k)_p\), \(m=(b_0b_1\dots b_k)_p\), 有
\[ \binom n m\equiv \prod _{i=0}^k\binom {a_i} {b_i} \pmod p \]

也即
\[ \binom n m \equiv \binom {\lfloor \frac{n}{p}\rfloor} {\lfloor \frac{m}{p}\rfloor} \cdot \binom {n \bmod p} {m \bmod p} \pmod p\]

递归求解.

时间复杂度 \(O(p \log_p n)\) , 或者 \(O(p)\) 预处理, \(O(\log_p n)\) 单次询问.

代码

ll c(ll n,ll m){
    if(m<0||m>n)return 0;
    return fact(n)*inv(fact(m)*fact(n-m)%p)%p;
}
ll lucas(int n,int m){//c(n,m)%p
    return m?lucas(n/p,m/p)*c(n%p,m%p)%p:1;
}

exLucas

【知识总结】扩展卢卡斯定理(exLucas) - Inspector_Javert - CSDN博客

p为合数.

分解质因数+阶乘取模+组合数+excrt

注意如果计算 \(n! \bmod p^k\) 时如果计算 \(p^x\) 对结果的贡献, 将无法求逆元. 因此需要求 \(\frac{n!}{p^x} \bmod p^k\), 即忽略p的幂, 然后在求组合数时再乘回来.

码量++

复杂度太长... 当它是 \(O(p \log p)\) 好惹

代码

ll qpow(ll a,ll b,ll nmod){
    ll res=1;
    while(b){
        if(b&1)res=res*a%nmod;
        a=a*a%nmod;
        b>>=1;
    }
    return res;
}

void exgcd(ll a,ll b,ll &x,ll &y,ll &d){
    b?(exgcd(b,a%b,y,x,d),y-=x*(a/b)):(x=1,y=0,d=a);
}
ll inv(ll a,ll m){
    ll x,y,d;
    exgcd(a,m,x,y,d);
    return x>=0?x%m:x%m+m;
}
ll crt(ll a,ll m,ll m0){//m0 | m; (m0,m/m0)=1
    return m/m0*inv(m/m0,m0)%m*a%m;
}

ll fact(ll n,ll p,ll pk){//(n!/p^x)%(p^k)
    if(n<=1)return 1;
    ll ans=1,tmp=n%pk;
    rep(i,1,pk){
        if(i%p)ans=ans*i%pk;
    }
    ans=qpow(ans,n/pk,pk);
    rep(i,1,tmp){
        if(i%p)ans=ans*i%pk;
    }
    return ans*fact(n/p,p,pk)%pk;
}

ll c(ll n,ll m,ll p,ll pk){//c(n,m)%(p^k)
    ll sum=0;
    for(ll i=n;i;i/=p)sum+=i/p;
    for(ll i=m;i;i/=p)sum-=i/p;
    for(ll i=n-m;i;i/=p)sum-=i/p;
    return qpow(p,sum,pk)*fact(n,p,pk)%pk*inv(fact(m,p,pk),pk)%pk*inv(fact(n-m,p,pk),pk)%pk;
}

ll fac[40][2],pf; //0 p;  1 pk
void getfac(ll n){
    ll tmp=sqrt(n);
    rep(i,2,tmp){
        if(n%i==0){
            fac[++pf][0]=i,fac[pf][1]=1;
            while(n%i==0)n/=i,fac[pf][1]*=i;
        }
    }
    if(n>1)fac[++pf][0]=n,fac[pf][1]=n;
}
ll exlucas(ll n,ll m,ll p){
    ll ans=0;
    getfac(p);
    rep(i,1,pf){
        ans=(ans+crt(c(n,m,fac[i][0],fac[i][1]),p,fac[i][1]))%p;
    }
    return ans;
}

转载于:https://www.cnblogs.com/ubospica/p/10363574.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值