有一个方法可以求,但对b,m有限制。
设 k= ⌊ a b ÷ m ⌋ {\lfloor{\frac{a}{b}\div m}\rfloor} ⌊ba÷m⌋ ,x = a b % m {\frac{a}{b}\% m} ba%m
⇒ a b {\frac{a}{b}} ba=k*m+x
⇒ a=k*m*b+x*b
⇒ a%(m*b)=x*b
⇒ x= a % ( m ∗ b ) b {\frac{a\%(m*b)}{b}} ba%(m∗b)
⇒ a b % m {\frac{a}{b}\% m} ba%m= a % ( m ∗ b ) b {\frac{a\%(m*b)}{b}} ba%(m∗b)
如果m*b很大甚至会爆long long , 这时就需要用另一种方法来完成。
当要求取模时, a b {\frac{a}{b}} ba答案可能为小数无法直接取模,那么此时就需要一个东西将分数转化为乘法的形式以帮助我们取模,这个“东西”就是逆元。
a b {\frac{a}{b}} ba%m = (a*inv(b))%m(此时inv(b)为b的逆元)
由上可知,决定一个数的逆元不仅取决它本身还取决取模的数。
条件:x存在乘法逆元的充要条件是x与模数m互质
以后面对除法取模即可通过逆元来实现
m为质数时费马小定理,m为非质数时欧拉定理。(Olog(n))
总的可以归纳为欧拉定理求解,只不过对于欧拉定理特例费马小定理来说,m为质数可以直接得出答案,避免求欧拉函数一项省去一部分时间复杂度。
费马小定理:
当m为质数时,
欧拉定理:
m不是质数
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define inf 0xffffff
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
const int mod=998244353;
const int N = 5e5+10;
int phi[N],vis[N],prime[N];
void get() //欧拉筛素数
{
phi[1]=1;
for(int i=2;i<N-10;i++)
{
if(!vis[i])
{
prime[++prime[0]]=i;
phi[i]=i-1;
}
for(int j=1;j<=prime[0]&&prime[j]*i<N-10;j++)
{
vis[prime[j]*i]=1;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
int euler_phi(int n)
{
int m=sqrt(n+0.5);
int ans=n;
for(int i=2;i<=m;i++)
{
if(n%i==0)
{
ans=ans/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) ans=ans/n*(n-1);
return ans;
}
ll qpow(ll a,ll b) //a^b快速幂
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a = a*a%mod;
b>>=1;
}
return ans%mod;
}
ll inv1(ll a) //费马小定理
{ return qpow(a, mod-2)%mod; }
ll inv2(ll a) //欧拉定理
{ return qpow(a, euler_phi(mod)-1); }
int main(int argc, char *argv[]) {
get();
int a;
cin >> a;
if(!vis[a]) cout << inv1(a);
else cout << inv2(a);
}
拓展欧几里得 (Olog(n))
设一个数为n,取模的数为m,gcd(n,m)==1。
nx+my=gcd(n,m)=1
两边同时对m取模
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define inf 0xffffff
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
const int mod=998244353;
const int N = 5e5+10;
void exgcd(int a,int b,int& d,int& x,int& y)
{
if(!b) { d=a; x=1; y=0; }
else { exgcd(b, a%b, d, y, x); y-=x*(a/b); }
}
int inv(int a)
{
int x,y,d;
exgcd(a, mod, d, x, y);
return (x+mod)%mod;
}
int main(int argc, char *argv[]) {
int a;
cin >> a;
cout << inv(a);
}
线性递推O(n)打表求[1,n]所有逆元
求x%m的逆元
m= ⌊ m x ⌋ ∗ x + m % x {\lfloor{\frac{m}{x}}\rfloor}*x+m\%x ⌊xm⌋∗x+m%x
∵ m ≡ 0 ( % m ) {\because}m{\equiv} 0(\% m) ∵m≡0(%m)
∴ ⌊ m x ⌋ ∗ x + m % x ≡ 0 ( % m ) {\therefore}{\lfloor{\frac{m}{x}}\rfloor}*x+m\%x{\equiv} 0(\% m) ∴⌊xm⌋∗x+m%x≡0(%m)
移项得
⇒ ⌊ m x ⌋ ∗ x ≡ − m % x ( % m ) {\lfloor{\frac{m}{x}}\rfloor}*x{\equiv}-m\%x(\% m) ⌊xm⌋∗x≡−m%x(%m)
两边同乘以inv(x)
⇒
⌊ m x ⌋ ∗ x ∗ i n v ( x ) ≡ ( − m % x ) ∗ i n v ( x ) ( % m ) {\lfloor{\frac{m}{x}}\rfloor}*x*inv(x){\equiv}(-m\%x)*inv(x)(\% m) ⌊xm⌋∗x∗inv(x)≡(−m%x)∗inv(x)(%m)
∵ x ∗ i n v ( x ) ≡ 1 % m o d {\because}{x*inv(x){\equiv}1\%mod} ∵x∗inv(x)≡1%mod
∴ ⌊ m x ⌋ ≡ ( − m % x ) ∗ i n v ( x ) ( % m ) {\therefore}{{\lfloor{\frac{m}{x}}\rfloor}{\equiv}(-m\%x)*inv(x)(\% m)} ∴⌊xm⌋≡(−m%x)∗inv(x)(%m)
移项得( − ⌊ m x ⌋ {-\lfloor{\frac{m}{x}}\rfloor} −⌊xm⌋为负数不好操作,加上m无可厚非)
⇒ m − ⌊ m x ⌋ ≡ ( m % x ) ∗ i n v ( x ) ( % m ) {{m-\lfloor{\frac{m}{x}}\rfloor}{\equiv}(m\%x)*inv(x)(\% m)} m−⌊xm⌋≡(m%x)∗inv(x)(%m)
两边同乘以inv(m%x)
⇒ ( m − ⌊ m x ⌋ ) ∗ i n v ( m % x ) ≡ ( m % x ) ∗ i n v ( x ) ∗ i n v ( m % x ) ( % m ) {{(m-\lfloor{\frac{m}{x}}\rfloor)*inv(m\%x)}{\equiv}(m\%x)*inv(x)*inv(m\%x)(\% m)} (m−⌊xm⌋)∗inv(m%x)≡(m%x)∗inv(x)∗inv(m%x)(%m)
同理得
⇒ i n v ( x ) ≡ ( m − ⌊ m x ⌋ ) ∗ i n v ( m % x ) ( % m ) {{inv(x){\equiv}(m-\lfloor{\frac{m}{x}}\rfloor)*inv(m\%x)}(\% m)} inv(x)≡(m−⌊xm⌋)∗inv(m%x)(%m)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define inf 0xffffff
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
const int mod=998244353;
const int N = 5e5+10;
ll inv[N];
int main(int argc, char *argv[]) {
inv[1]=1;
for(int i=2;i<N-10;i++)
inv[i]=((mod-mod/i)*(inv[mod%i])+mod)%mod;
int a;
cin >> a;
cout << inv[a];
}