模运算:取余运算,即a除以b获得的余数,记为mod,又记为%c++
模运算过程当中,加减乘均可以先对a,b进行%p,而后再进行加减乘,最后再%p,结果不变ui
运算符优先级,模运算和乘除法的运算符优先级是同样的spa
同余:a和b除以p获得的余数相同,即p能够整除(a-b)code
求解\(\cfrac{a}{b} \mod p\)的值,由于除法不能直接对a,b先取模再进相除,因此这边就引入了逆元get
逆元,能够理解为是在mod p意义下b的倒数,下文中将b的逆元记录为inv[b]
如下是逆元的定义
\(b \times inv[b] \equiv 1 \pmod p\)it
1.第一个性质
\(\cfrac{a}{b} \equiv a \times inv[b] \pmod p\)
证实以下
\(b \times inv[b] \equiv 1 \pmod p\)
\((b \times inv[b]-1)\bmod p=0\)
由模运算的乘法性质\(a \times b \bmod p=(a \bmod p) \times (b \bmod p)\bmod p\)
获得\(\cfrac{a}{b}\times(b \times inv[b]-1)\bmod p=0\)
\((a \times inv[b]- \cfrac{a}{b})\bmod p=0\)
\(a \times inv[b] \equiv \cfrac{a}{b} \pmod p\)
2.惟一性
设b有两个逆元c,d
则\(a*c \equiv a*d \pmod p\)
\((a*c-a*d)\pmod p=0\)
\(a(c-d) \mod p=0\)
\(a\% p \neq 0\)
由模运算的乘性质获得\(c=d\)
因此b的逆元惟一
3.可积性
\(inv[a] \times inv[b]=inv[a \times b]\)
证实
\(a*b * inv[ab]\equiv \pmod p\)
又由于\(a*inv[a]\equiv 1\)
\(b*inv[b]\equiv 1\)
模运算乘性质易证可积性成立
4.周期性
求解\(\cfrac{a}{b} \mod p\)中b的逆元
通常默认b小于p,可是当b大于p的时候来求逆元,其实就是一个周期性
即\(inv[k*p+r]=inv[r],0<r<p,k \in N^*\)
证实
由于同余,知足两边同乘以一个数依旧成立
因此有如下
设\(\cfrac{n}{r} \equiv k_1 \pmod p\)
\(\cfrac{n}{tp+r} \equiv k_2 \pmod p\)
则对于第二个式子,两边同时乘以\(tp+r\)有
\(n \equiv k_2r \pmod p\)
\(\cfrac{n}{r} \equiv k_2 \pmod p\)
同理\(\cfrac{n}{r} \equiv k_1 \pmod p\)
即\(k_1 \equiv k_2 \pmod p\)
设\(b=r,c=t*p+r\),则有
\(\cfrac{n}{b} \equiv \cfrac{n}{c} \pmod p\)
\(n*inv[b] \equiv n*inv[c] \pmod p\)
\(inv[b] \equiv inv[c] \pmod p\)
又由于\(0\le inv[b],inv[c] <p\)
因此\(inv[b]=inv[c]\)class
费马小定理和拓展欧几里得这边就不讲了
这边主要讲线性求逆元
一下模的结果范围为[0,r-1]
\(p=k*i+r\)
\(k*i+r \equiv 0 \pmod p\)
模运算乘性质有
式子两边同时乘以\(inv[i]*inv[r]\)有
\(k*inv[r]+inv[i] \equiv 0 \pmod p\)
\(inv[i] \equiv k*inv[r] \pmod p\)
\(inv[i] \equiv -\cfrac{p}{i}*inv[p \% i] \pmod p\)
其中p%i在[0,i-1]范围内,即求i的时候p%i的逆元已经被求出
1.线性求逆元
原题见,洛谷P3811乘法逆元模版题im
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mx=20000999; ll inv[mx]; void getinv(ll n,ll p){ inv[1]=1; for(int i=2;i<=n;i++){ inv[i]=(1ll*(p-(p/i))*inv[p%i])%p; //由于C++对于负数取模仍是负数因此须要+p //中间相乘可能会爆int范围,因此用ll } } int main(){ int n,p; scanf("%d%d",&n,&p); getinv(n,p); for(int i=1;i<=n;i++){ printf("%d\n",inv[i]); } }
2.线性求单个逆元模版
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mx=20000999; ll inv[mx]; ll getinv(int x,int p) { return x==1?1:1ll*(p-p/x)*getinv(p%x,p)%p; } int main() { int n,p; scanf("%d%d",&n,&p); cout<<getinv(n,p); }