zoukankan      html  css  js  c++  java
  • 【学习笔记】$gcd$ 与扩展 $gcd$

    前言

    模板测试!我!居然!没写出来!这足以证明之前的我学习方法是有多蠢 (/kk)

    重学重学唉/(ㄒoㄒ)/~~

    声明:本博客所有随笔都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。

    (\)


    (\)

    (gcd)

    欧几里得算法也叫辗转相除法,是用来求两个数的最小公倍数的。

    主要运用的定理:

    [gcd(a, b) = gcd(b, a\%b) ]

    特殊的,当 (b = 0) 时,(gcd(a, b) = a)

    于是可以递归下去直到 (b = 0)

    时间复杂度是 (O(log n)) 级别的,但具体的要运用的一些奇怪定理,暂时略过了。

    (Code:)

    int gcd(int a, int b){return b == 0 ? a : gcd(b, a % b);}
    

    (\)


    (\)

    扩展 (gcd)

    一步一步来推。

    (1st.) 对于 (ax + by = gcd(a, b)) 这个方程, (x, y) 有整数解

    根据欧几里得算法可得,(ax + by = gcd(a, b) = gcd(b, a \% b) = bx_1 + (a \% b)y_1)

    [ax + by = bx_1 + (a \% b)y_1 ]

    [ax + by = bx_1 + (a - (a / b) * b)y_1 ]

    [ax + by = bx_1 + ay_1 - (a / b) * by_1 ]

    [ax + by = ay_1 + b(x_1 - (a / b)y_1) ]

    于是可以得到,(x = y_1, y = (x_1 - (a / b)y_1))

    特殊的,当 (b = 0) 时,(x = 1, y = 0)

    于是可以一直递归下去到 (b = 0) 时,在返回不断更新解

    (Code:)

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

    (\)

    (2nd.) 对于一般方程 (ax + by = c) 的求解

    首先,(c) 必须是 (gcd(a, b)) 的倍数,可以理解为,把方程左边提一个因数 (gcd(a, b)) 出来,那么右边也得有这个因数

    在第一步时,我们对于特殊方程 (ax + by = gcd(a, b)) 求出了一组唯一解 (x_0, y_0)

    (c / gcd(a, b) = k),那么方程的一组特殊解为 (x_1 = x_0 * k, y_1 = y_0 * k)

    接下来求一般解,其实没有想象的那么难!(感觉网上的博客讲得玄乎其神的...也许是我太菜了 (/kk)

    设方程某个解为 (x = x_1 + k, y = y_1 + k'),即 (a(x_1 + k) + b(y_1 + k') = c)

    把式子拆开即可得 (ax_1 + by_1 + ak + bk' = c)

    发现 (ak + bk' = 0) ,把 (a, b) 拆开写成 (gcd(a, b) * (a'k + b'k') = 0) ,此时 (gcd(a', b') = 1)

    那么仍然要满足上面的式子,就要求 (k = b', k' = -a') ,即 (k = frac{b}{gcd(a, b)}, k' = - frac{a}{gcd(a, b)})

    于是就顺利得到了新的一组解—— (x = x_1 + frac{b}{gcd(a, b)}, y = y_1 - frac{b}{gcd(a, b)}),只要把这组 (x, y) 看做 (x_1, y_1) ,按照上面的步骤继续做就可以得到其他解了~

    然后可以得到解的通式

    [x = x_1 + frac{b}{gcd(a, b)} * k, y = y_1 - frac{a}{gcd(a, b)} * k ]

    显然,这样的解是无数的,我们主要来讨论一下正整数解,即 ((x, y)) 满足 (x > 0, y > 0)

    (3rd.) 关于 (ax + by = c) 的正整数解的讨论

    再复制一下方程的解的通式

    [x = x_1 + frac{b}{gcd(a, b)} * k, y = y_1 - frac{a}{gcd(a, b)} * k ]

    则可得到两个不等式:

    [egin{cases}y_1 - frac{a}{gcd(a, b)} * k > 0\x_1 + frac{b}{gcd(a, b)} * k > 0end{cases} ]

    解得

    [- frac{x_1gcd(a, b)}{b} < k < frac{y_1gcd(a, b)}{a} ]

    要求 (k) 为整数,即

    [lceil- frac{x_1gcd(a, b)}{b} ceil < k < lfloor frac{y_1gcd(a, b)}{a} floor ]

    即可求出 (k_{max}, k_{min}),则方程正整数解的数量就是 (k_{max} - k_{min} + 1)

    (k_{max}, k_{min}) 带入解的通式,则可得到 (x)(y) 的最大最小正整数解

    如果不等式无解,则方程无正整数解

    那么 (x) 的最小正整数取值的 (k) 就为 (lceil- frac{x_1gcd(a, b)}{b} ceil)

    (y) 的最小正整数取值的 (k) 就为 (lfloorfrac{y_1gcd(a, b)}{a} floor)

    (\)


    (\)

    模板题

    丑陋的代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define F(i, x, y) for(register int i = x; i <= y; ++ i) 
    using namespace std;
    inline ll read();
    ll t, a, b, c, x, y, res;
    ll exgcd(ll a, ll b, ll &x, ll &y)
    {
    	if(b == 0) 
    	{
    		x = 1, y = 0;
    		return a;
    	}
    	ll res = exgcd(b, a % b, x, y), tmp = y;
    	y = x - (a / b) * y, x = tmp;	
    	return res;
    }
    int main()
    {
    	t = read();
    	while(t --)
    	{
    		a = read(), b = read(), c = read();
    		res = exgcd(a, b, x, y);
    		if(c % res != 0) {puts("-1"); continue;}
    		x = x * (c / res);
    		y = y * (c / res);
    		ll k1 = ceil(-x * 1LL * res / b);
    		while(x + b / res * k1 <= 0) ++ k1;
    		ll k2 = floor(y * 1LL * res / a);
    		while(y - a / res * k2 <= 0) -- k2;
    		ll minx = x + b / res * k1;
    		ll maxx = x + b / res * k2;
    		ll miny = y - a / res * k2;
    		ll maxy = y - a / res * k1;
    		if(k2 >= k1)
    			printf("%lld %lld %lld %lld %lld
    ", k2 - k1 + 1, minx, miny, maxx, maxy);
    		else printf("%lld %lld
    ", minx, miny);
    	}
    	return 0;
    }
    inline ll read()
    {
    	ll x = 0;
    	char c = getchar();
    	while(c < '0' || c > '9') c = getchar();
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x;
    }
    

    完结撒花✿✿ヽ(°▽°)ノ✿

  • 相关阅读:
    activiti--操作例子
    activiti--服务表
    spring--加载资源文件
    Day17
    Day15
    Day14
    Day13
    Day12
    Day16
    Day11
  • 原文地址:https://www.cnblogs.com/Bn_ff/p/12865574.html
Copyright © 2011-2022 走看看