目录
1)判断方程ax+by=k是否有解:k | gcd ( a,b )
1.常见结论
2.最大公约数 最小公倍数
gcd:除数/余数 (欧几里得 辗转相除法)
lcm:ab / gcd( a,b )
inline int gcd(int a,int b){
int r;
while(b){
r=a%b;
a=b,b=r;
}return a;
}
inline int lcm(int a,int b){
return 1ll*a/gcd(a,b)*b;
}
3. 扩展欧几里得
ax + by = gcd( a,b ) 的解一定存在
1)判断方程ax+by=k是否有解:k | gcd ( a,b )
2)求ax+by=k的任意一组解、通解、最小整数数
- 任意一组解:x0 = x0 * (k / gcd( a,b )) y0 = y0 * (k / gcd( a,b ))
- 通解:
- x=x0 + b / gcd(a,b) * i (相当于x每次可以增减:b/gcd的整数倍)
- y=y0 - a / gcd(a,b) * i (相当于y每次可以减增:a/gcd的整数倍)
- 最小整数解:
x= ( x + b / gcd *i ) % ( b / gcd ) = x%( b / gcd ) (b/gcd(a,b)应取正) 若 x <= 0,则 x += b/gcd
3)求逆元
若 a 关于 p 的逆元 x 存在,则 gcd ( a , p ) = 1,a x ≡ 1 mod p
我们可以得到 a x + p k = 1 ,其实就是 ax + pk = gcd( a,p )
所以求出一个最小的 x 就是 a 关于 p 的一个逆元
inline int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int _x,_y;
int d=exgcd(b,a%b,_x,_y);
x=_y;
y=_x-a/b*x;
return d;
}
int main(){
int a,b,k,x,y;
scanf("%d%d%d",&a,&b,&k);
int d=exgcd(a,b,x,y);
//1.解是否存在
bool f=0;
if(k%d==0) f=1;
//2.求解
//任意
x=x*(k/d); y=y*(k/d);
//通解
x=x+b/d*i; y=y-a/d*i;//i任意
//最小整数解
x=x%(b/d);
if(x<0)x+=b/d;
//3.求逆元x
if(d==1)x;
}
3. 逆元
逆元概念_三种求法 (费马小定理,扩展欧几里得推导,递推求阶乘逆元)
若xy≡1 (mod p),且gcd(x,p)=1(gcd函数是求x,y的最大公约数),则称x关于模p的乘法逆元为y。
那么除以y相当于乘以x(模p情况下)。值得注意的是x(y+k*p)≡1(mod p),逆元不止一个,求最小的就可以;
- 费马小定理:
- 扩展欧几里得:a x + b y = gcd ( a , b ) 的解一定存在。ax+pk=1,可求出一个最小的 x 就是 a关于 p 的一个逆元
-
递推求阶乘逆元:
1)费马小定理
gcd(a,p)=1,那么 a^{p-2} * a ≡ 1mod p,可得求 a 的逆元,就直接用快速幂求 a^{p-2}
inline ll quickp(ll base,ll pow=mod-2){
ll res=1;
while(pow){
if(pow&1)res=res*base%mod;
pow>>=1;
base=base*base%mod;
}return res;
}
inline ll inv(ll x){
return quickp(x,mod-2);
}
2)线性算法
设t=p%i , r=p/i ; 则可以得到 i*r+t=p -->i * r+t ≡ 0(mod p)
式子两边同时乘以i-1 * t-1 (i,t的逆元)得 t-1*r+ i-1≡0(mod p)
即i-1≡(-r)*t-1(mod p) , 由t=p%i可得t<i,所以 i 的逆元可以由比 i 小的 t 的逆元求出来
因为逆元是正数,而此时i-1的为负值,所以 i-1≡(-r)*t-1+t-1 * p≡(p-r)*t-1(mod p)(加上t-1倍的p后逆元就是正的了,但是要记得取模)
那么化简并带入 t=p%i,r=p/i 可得 i-1=(p-p/i)* (p%i)-1%p
typedef long long ll;
ll inv[3000010];
void line(ll n,ll p)
{
inv[1]=1;
for(int i=2;i<=n;i++)
{
inv[i]=(p-p/i)*inv[p%i]%p;
}
}
3)扩展欧几里得
4)递推求阶乘逆元
用费马小定理先求出最大那个阶乘的逆元在递归
用预处理的阶乘和阶乘逆元计算组合数
int fac[N],ifac[N];//阶乘,阶乘的逆元 初始化1e6的数组大约用200MS。1秒的时限处理不完1e7
inline ll quickp(ll base,ll pow=mod-2){
ll res=1;
while(pow){
if(pow&1)res=res*base%mod;
pow>>=1;
base=base*base%mod;
}return res;
}
inline void init(){
fac[0]=ifac[0]=1;
for(int i=1;i<N;i++)fac[i]=1ll*fac[i-1]*i%mod;
ifac[N-1]=quickp(fac[N-1]);
for(int i=N-2;i;i--)ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
}
inline int C(int n,int m)//求组合数{
if(n<m) return 0;
return 1ll*fact[n]*ifact[m]%mod*ifact[n-m]%mod;
}
相关习题
P3811 【模板】乘法逆元 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 费马小定理>扩展欧几里得会超时>线性算法
Problem - 1576 (hdu.edu.cn) 快速幂求逆元
Problem - 2669 (hdu.edu.cn) exgcd求最小整数解
Problem - 5698 (hdu.edu.cn) 递推预处理阶乘逆元+组合数 / 扩展欧几里得+组合数(打表找规律发现杨辉三角)
1061 -- 青蛙的约会 (poj.org) exgcd求最小整数解