zoukankan      html  css  js  c++  java
  • GCD 及 EXGCD 复习笔记

    GCD

    辗转相除法

    证明

    设实数 (a)(b) ,我们要证明 $$gcd(a,b) == gcd(b,a % b) $$

    1. 证明 $gcd(a,b) | (a-b) $

      可以发现,(a==km)(b==lm)((a-b)==(k-l)m quad(m in N^*))

      (gcd(a,b)==m),易知 $gcd(a,b) | (a-b) $ 。

      同理

      (a-b=kp)(b=lp) ,那么 (a=(k+l)p) ,所以 (gcd(a-b,b)|a)

    2. 由 $ 1 $ 可得,(gcd(a,b))(b,a-b) 的公因数,且 (gcd(a-b,b))(a,b) 的公因数。由 (gcd) 的定义可知 (gcd(a,b) leq gcd(b,a-b))(gcd(a,b) geq gcd(b,a-b))(gcd(a,b) == gcd(b,a-b))

    3. (a) 重复减去 (b) 其实就是 取模。

    Code

    很简单,直接贴了。

    int gcd(int a,int b){
       return b==0?a:gcd(b,a%b);
    }
    
    • 注意 (gcd) 只能处理正数,负数处理方法见线性同余方程。

    • 顺带一提,(lcm)(最小公倍数) ( imes gcd = a imes b)

    luogu P1029

    EXGCD

    原理要用到 (gcd) ,但用处和 (gcd) 关联其实不是很大。

    裴蜀定理

    方程

    [ax+by=gcd(a,b) ]

    至少有一组整数解。

    证明

    不难看出问题可以简化成:

    [ax+by=gcd(a,b) iff mx+ny=1 ]

    其中,(m)(n) 互质。即证明两个互质的数可以线性组合出 (1)

    (gcd) 的过程我们发现,辗转相除的实质就是更相减损,即用两数加减组合。即两个互质的数可以组合出 (1) (也就是任何数)。

    exgcd

    根据裴蜀定理,我们知道,对于一个二元一次方程:

    [ax+by=c ]

    (c)(gcd(a,b)) 的整数倍时,方程有整数解。

    那么如何解这个方程呢?

    原理

    (gcd(a,b)|c) 时 ,我们将等式两边同除以 (frac{c}{gcd(a,b)})

    [ax+by=c iff a imes frac{x}{c}+b imesfrac{y}{c}=gcd(a,b) ]

    (frac{x}{c})(frac{y}{c})(x')(y'),根据 (gcd) 的原理我们知道:

    [b imes x''+a\%b imes y''= gcd(b,a\% b)\ implies b imes x''+(a-b imes lfloorfrac{a}{b} floor) imes y''=gcd(b,a\%b)\ implies a imes y''+b imes (x''-lfloorfrac{a}{b} floor imes y'')=gcd(b,a\%b)=gcd(a,b)\ implies a imes y''+b imes (x''-lfloorfrac{a}{b} floor imes y'')=ax'+by' \ herefore x'=y''qquad y'=x''-lfloorfrac{a}{b} floor imes y'' ]

    这样不断递归下去,直到:

    [gcd(a,b) imes x'''+0 imes y'''=gcd(a,b)\ herefore x'''=1qquad y'''=0 ]

    然后回溯回去求得 (x)(y)

    Code

    void exgcd(int a,int b,int &x,int &y){
    	if(b==0){
    		x=1;y=0;
    	}
    	else{
    		exgcd(b,a%b,y,x);
    		y-=(a/b)*x;
    	}
    }
    

    线性同余方程*

    母题

    求解

    [ax equiv b (mod p) ]

    原式可以如下变换:

    [(a imes x)\%p=b\%p iff (a imes x-b)\%p=0\ implies ax+b=py \ implies ax+p(-y)=b ]

    这样我们就能用 (exgcd) 求解了。

    这里要注意,(exgcd) 求解的是

    [ax+p(-y)=gcd(a,p) ]

    我们要给求出的解进行处理才行。

    下面我们来上题

    例题

    luogu P1082 MOD

    模板啦直接贴代码

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    void exgcd(int a,int b,int &x,int &y){
    	if(b==0){
    		x=1;
    		y=0;
    	}
    	else{
    		exgcd(b,a%b,y,x);
    		y-=(a/b)*x;
    	}
    }
    
    int main()
    {
    	int a,b;
    	int x,y;
    	scanf("%d %d",&a,&b);
    	//a,b互质 
    	exgcd(a,b,x,y);
    	printf("%d",((x%b)+b)%b);
    	return 0;
    }
    

    [luogu P1516] 青蛙ha的约会

    1. 题目大意:

    求满足 (x+k imes n equiv y+k imes m quad (mod ; l)) 的最小的 (k)

    1. 推柿子:

    [(x+k imes n )\%l = (y+k imes m)\%l\ implies (x-y)+(n-m) imes k=l imes lambda\ implies (m-n) imes k+l imes lambda=x-y ]

    然后几乎就转化成裸题辽。

    1. 细节

    我们发现 (m-n) 可能是负数,但是 (exgcd) 只能处理正数,所以要小小变换一下。因为我们只需要知道 (k) ,就可以这样:

    [(m-n) imes k+l imes lambda=x-y iff (n-m) imes k+l imes (-lambda)=y-x ]

    1. (Code)
    #include<cstdio>
    #include<algorithm>
    typedef int int_;
    #define int long long
    using namespace std;
    
    int n,m,wa,wb,l;
    
    int mod(int o,int p){
    	return ((o%p)+p)%p;
    }
    
    int gcd(int a,int b){
    	return b==0?a:gcd(b,a%b);
    }
    
    void exgcd(int a,int b,int &x,int &y){
    	if(b==0){
    		x=1;y=0;
    	}
    	else{
    		exgcd(b,a%b,y,x);
    		y-=(a/b)*x;
    	}
    }
    
    
    
    int_ main()
    {
    	scanf("%lld %lld %lld %lld %lld",&wa,&wb,&n,&m,&l);
    	wa=mod(wa,l);wb=mod(wb,l);
    	int a=m-n,b=l,c=wa-wb;
        if(a<0){
        	a=-a;
        	c=-c;
    	}
    	int g=gcd(a,b);
    	if(c%g != 0){
    		printf("Impossible");
    		return 0;
    	}
    	a/=g;b/=g;c/=g;
    	int x,y;
    	exgcd(a,b,x,y);
    	x*=c;
    	x=mod(x,b);
    	printf("%lld",x);
    	return 0;
    }
    

    [luogu P2421] 荒岛野人

    1. 题目大意

    和上题稍有不同,这题是两两求(c_i+k imes p_i equiv c_j+k imes p_jquad (mod; M))(min(l_i,l_j)) 内无正整数解。

    1. 做法

    很简单,只需从小到大枚举(M) ,然后检验每两个人都不会相遇就ok。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define maxn 1000000
    
    int n,maxx,c[20],p[20],l[20];
    
    int mod(int o,int M){
    	return ((o%M)+M)%M;
    }
    
    int gcd(int a,int b){
    	return b==0?a:gcd(b,a%b);
    }
    
    void exgcd(int a,int b,int &x,int &y){
    	if(b==0){
    		x=1,y=0;
    	}
    	else{
    		exgcd(b,a%b,y,x);
    		y-=(a/b)*x;
    	}
    }
    
    bool work(int i,int j,int m){
    	int a=p[i]-p[j],b=m,d=c[j]-c[i];
    	if(a<0){
    		a=-a,d=-d;
    	}
    	int g=gcd(a,b);
    	if(d%g != 0){
    		return false;
    	}
    	a/=g,b/=g,d/=g;
    	int x,y;
    	exgcd(a,b,x,y);
    	x*=d;
    	x=mod(x,b);
    	if(x <= min(l[i],l[j])) return true;
    	else return false;
    }
    
    bool check(int m){
    	for(int i=1;i<n;i++){
    		for(int j=i+1;j<=n;j++){
    			if(work(i,j,m)){
    				return false;
    			}
    		}
    	}
    	return true;
    }
    
    
    
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d %d %d",&c[i],&p[i],&l[i]);
    		maxx=max(maxx,c[i]);
    		c[i]--;
    	}
    	for(int i=1;i<=maxn;i++){
    		if(check(i)){
    			printf("%d",max(maxx,i));
    			return 0;
    		}
    	}
    	return 0;
    }
    

    内容采用“知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议”进行许可。请您在转载时注明来源及链接。


    • (frak byquad thorn\_)
  • 相关阅读:
    软件工程 speedsnail 第二次冲刺2
    软件工程 speedsnail 第二次冲刺1次
    软件工程 speedsnail 冲刺9
    软件工程 speedsnail 冲刺8
    软件工程 speedsnail 冲刺7
    软件工程 speedsnail 冲刺6
    软件工程 speedsnail 冲刺5
    软件工程 speedsnail 冲刺4
    软件工程 speedsnail 冲刺3
    软件工程 speedsnail 冲刺2
  • 原文地址:https://www.cnblogs.com/thornblog/p/11715805.html
Copyright © 2011-2022 走看看