欧几里得算法,也叫辗转相除,简称 gcd,用于计算两个整数的最大公约数
定理:gcd(a,b)==gcd(b,a%b)
附带可能会用到的:gcd(a,b)==gcd(a,a-b)==gcd(a,a+b) gcd(a,b,c)==gcd(a,b-a,c-b)
int gcd(int a, int b)//最大公约数
{
return b == 0 ? a : gcd(b, a % b);
}
int lcm(int a, int b)//最小公倍数
{
return a / gcd(a, b) * b;
}
扩展欧几里得算法,简称 exgcd,一般用来求解不定方程,求解线性同余方程,求解模的逆元等
引理:存在 x , y 使得 gcd(a,b)=ax+by
当 b=0 时,gcd(a,b)=a,此时 x2= 1, y2=0 因此只要从此处往前处理,在进行欧几里德算法的递归的时候根据相邻两次调用间x1,y1和x2,y2的关系 x1=y2 , y1=x2-a/b*y2 计算即可求解ax+by=gcd(a,b)的解.
int exgcd(int a, int b, int&x, int&y) //拓展欧几里得算法
{
if(!b)
x = 1, y = 0;
return a;
else
{
int ans=exgcd(b, a % b, x, y);
int tmp=x;
x=y;
y=tmp-a/b*y;
return ans;
}
}
对于 ax+by=c 的不定方程,设 r=gcd(a,b)
当 c%r!=0 时无整数解
当 c%r=0 时,将方程右边 *r/c 后转换为 ax+by=r 的形式
可以根据扩展欧几里得算法求得一组整数解 x0 , y0
而这只是转换后的方程的解,原方程的一组解应再 *c/r 转变回去
则原方程解为 x1=x0*c/r , y1=y0*(c/r);
通解 x=x1+b/r*t , y=y1-a/r*t ,其中 t 为整数
ll x,y,r,s;
void exgcd(ll a, ll b, ll &x, ll &y) //拓展欧几里得算法
{
if(!b)
x = 1, y = 0;
else
{
exgcd(b, a % b, y, x);
y -= x * (a / b);
}
}
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
void BD(ll a,ll b,ll c,ll r)
{
exgcd(a,b,x,y);
x=x*c/r;//得到原方程的解x和y
y=y*c/r;
}
int n,k1,k2;
cin>>n>>k1>>k2;
//k1*x+k2*y==n
int x=0,y=0;
int gcd=exgcd(x,y,k1,k2);
int modx=abs(y/gcd);
int mody=abs(x/gcd);
x=(x%modx+modx)%modx; //x的最小整数解
y=(y%mody+mody)%mody; //y的最小整数解
#include<iostream>
using namespace std;
#define int long long
int x,y,n,m,l;
int a,b,c;
int exgcd(int&x,int&y,int a,int b){
if(b==0){
x=1;
y=0;
return a;
}
int ans=exgcd(x,y,b,a%b);
int tmp=x;
x=y;
y=tmp-a/b*y;
return ans;
}
signed main(){
cin>>x>>y>>m>>n>>l;
a=n-m;
b=l;
c=x-y;
x=0;y=0;
int gcd=exgcd(x,y,a,b);
if(c%gcd==0){
x=x*c/gcd;
if(gcd<0) gcd=-1*gcd;
if(x<0){
while(x<0) x+=b/gcd;
}
else{
while(x>0&&x-b/gcd>0) x-=b/gcd;
}
cout<<x<<endl;
}
else{
cout<<"Impossible"<<endl;
}
}
除法逆元的三种求法:
逆元用处:(A/B)%p!=((A%p)/(B%p))%p 求 (A / B) %p ;在B的值非常大的情况下,B作为除数,极有可能会爆精度,除数不能太大;所以我们可以把他转化为乘法来解决
逆元存在条件: 求a关于p的逆元,逆元存在要求a和p互质
则 所以 b*inv[b]=1mod p 所以求解b的逆元inv[b]即求解b*inv[b]=1mod p
1.费马小定理 要求 1.a与p互质 2.p是质数
inv(a)=a^(p-2) mod p
LL ksm(LL a, int x) {
LL ans = 1;
while(x) {
if(x&1) ans = (ans * a) %mod;
a = (a * a) %mod;
x >>= 1;
}
return ans;
}
LL inv(LL a) {
return ksm(a, mod - 2);
}
2.扩展欧几里得求逆元 要求 1.a与p互质 (比费马小定理求解要快)
ax ≡ 1(mod p) => a*x+p*y = 1 求解出x即可 如果gcd不为1则说明a与p不互质不存在逆元
typedef long long ll;
void extgcd(ll a,ll b,ll& d,ll& x,ll& y){
if(!b){ d=a; x=1; y=0;}
else{ extgcd(b,a%b,d,y,x); y-=x*(a/b); }
}
ll inverse(ll a,ll n){
ll d,x,y;
extgcd(a,n,d,x,y);
return d==1?(x%n+n)%n:-1; //最小整数解
}
3. 有时会遇到这样一种问题,在模质数p下,求1~n逆元 n< p(这里为奇质数)。可以O(n)求出所有逆元,有一个递推式如下
当p是个质数的时候有 inv(a) = (p - p / a) * inv(p % a) % p
typedef long long ll;
const int N = 1e5 + 5;
int inv[N];
void inverse(int n, int p) {
inv[1] = 1;
for (int i=2; i<=n; ++i) {
inv[i] = (ll) (p - p / i) * inv[p%i] % p;
}
}
求解C(n,m) 采用打表把1-n的阶乘关于p的逆元求出来,要求1-n的阶乘关于p的逆元,可以先把1-n关于p的逆元求出来
C(n,m)==n!/m!/(n-m)!=n!*inv[m!]*inv[(n-m)!]
int fac[maxn];
int inv[maxn];
void init(int n){//逆元打表
fac[0]=1;
for(int i=1;i<=n;i++){//阶乘
fac[i]=fac[i-1]*i%mod;
}
inv[0]=1;
inv[1]=1;
for(int i=2;i<=n;i++){//1~n在 M 下的逆元
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
for(int i=2;i<=n;i++){//1~n的阶乘在 M 下的逆元
inv[i]=inv[i]*inv[i-1]%mod;
}
}
int comb(int a,int b){//求组合数
if(b==0){
return 1;
}
if(b<0||b>a){
return 0;
}
if(a==b){
return 1;
}
return fac[a]*inv[b]%mod*inv[a-b]%mod;//组合数公式
}
求解A(n,m) A(n,m)=n!/(n-m)!=n!*inv[(n-m)!]
int fac[maxn];
int inv[maxn];
void init(int n){//逆元打表
fac[0]=1;
for(int i=1;i<=n;i++){//阶乘
fac[i]=fac[i-1]*i%mod;
}
inv[0]=1;
inv[1]=1;
for(int i=2;i<=n;i++){//1~n在 M 下的逆元
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
for(int i=2;i<=n;i++){//1~n的阶乘在 M 下的逆元
inv[i]=inv[i]*inv[i-1]%mod;
}
}
int aomb(int a,int b){//求组合数
if(b==0) return 1;
if(b<0||b>a) return 0;
if(a==b) return fac[a];
return fac[a]*inv[a-b]%mod;//组合数公式
}
一个关于减法余数的易错点 (A-B)%mod==(A%mod-B%mod+mod)%mod
C(n,m)=n!*inv[m!]*inv[(n-m)!] 还可以采用线性递推的方法 C(n,m)=C(n-1,m)+C(n-1,m-1)
从n个物品中取m个有2种情况:(1)不取第n个物品,于是从前n-1个中取m个; (2)取第n个物品,于是从前n-1个中取m-1; 所以答案是这两种情况的和
int c[100][100];
void init(){
for(int i=0;i<=65;i++){
c[i][0]=1;
for(int j=1;j<=i;j++){
c[i][j]=c[i-1][j-1]+c[i-1][j];
}
}
}