首页 > 其他分享 >关于exgcd的总结

关于exgcd的总结

时间:2023-09-04 19:23:32浏览次数:46  
标签:总结 int dfrac ll exgcd yy 关于 ax

关于exgcd的总结

我们主要讨论的是\(ax+by=c\)

1.exgcd算法

1.1 关于解的存在性

裴蜀定理知,对于方程\(ax+by=c\)存在解的充分必要条件是:\((a,b)|c\)

tips:裴蜀定理 如果\(a,b\)均为整数,则有整数\(x,y\)使得\(ax+by=\gcd(a,b)\),这个等式称为裴蜀等式。

1.2exgcd算法介绍

要求\(ax+by=c\),我们可以用\(exgcd\)求出\(ax+by=\gcd(a,b)\)。令\(d = \gcd(a,b)\)。

当我们求出\(ax+by=d\)之后呢,求出的\(x,y\)乘上\(\dfrac{c}{d}\)就是答案啦。

具体处理方法就是:\(ax+by=(a,b)=(b,a\bmod b) = bx'+(a-b\times\lfloor\dfrac{a}{b}\rfloor)y' = ay'+b(x'-\lfloor\dfrac{a}{b}\rfloor y')\)

然后对比两边得到:\(x = y',y = x'-\lfloor\dfrac{a}{b}\rfloor y'\)然后只要一直递归下去就\(ok\)啦。

注意边界条件:\(b = 0\)时候,\(x = 1,y = 0\)是解。

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

1.3解的通式

对于\(ax+by=c\),假设它的其中一个特解是\(x_0,y_0\),那么我们可以求出它的通解:

\[\begin{cases} x^*=x_0+\dfrac{b}{(a,b)}t\\ y^*=y_0-\dfrac{a}{(a,b)}t \end{cases} \]

正确性也很显然,我们感性的去理解一下,因为要保证原式的值不变,那么\(a,b\)应该变化相同的倍率。

1.4关于\(exgcd\)的解

我们知道,对于\(ax+by=c\)的题解是:

\[\begin{cases} x^*=x_0+\dfrac{b}{(a,b)}t\\ y^*=y_0-\dfrac{a}{(a,b)}t \end{cases} \]

\(Q1:\)方程是否有正整数解?

我们发现,

  1. 当\(a,b\)同号时:\(x^*,y^*\)负相关,即\(x^*\)越大\(y^*\)越小。当求出的\(x\)是最小的时候,有\(y\)是最大的。

  2. 当\(a,b\)异号时,\(x^*,y^*\)正相关,即同增同减。

所以,一个合理的想法,我们对\(exgcd\)传参时候保证\(a,b\)的非负性即:若\(a<0,b>0\),那么传入\(exgcd(-a,b,x,y)\),最后注意讨论变化。

\(Q2:\)那么如何求解最小正整数解?

由通解可知:$x^*=x_0+\dfrac{b}{(a,b)}t $

(*)\(x_{min} = x_0 \bmod \dfrac{b}{(a,b)}\),相当于保证\(>0\)的情况下减去尽可能多的\(\dfrac{b}{(a,n)}\)。

通过\(x_{min}\)就很容易得到\(y_{max} =\dfrac{c-ax_{min}}{b}\)

\(ax+by=d\)的\(x\)最小的非负整数解\((x,y)\)的板子:

ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0)
    {
        x = 1; y = 0;
        return a;
    }
    ll xx,yy;
    ll d = exgcd(b, a % b, xx, yy);
    x = yy;
    y = xx - (a / b) * yy;
    return d;
}

int main()
{
    int _;cin>>_;
    while(_--)
    {
        ll a, b, x, y, m;
        scanf("%lld%lld%lld", &a, &b, &m);
        ll d = exgcd(a, b, x, y);
        if(m % d != 0)
        {
            puts("-1");
            continue;
        }
        a /= d;
        b /= d;
        m /= d;
        ll xx = (ll) x * (m % b) % b;
        if(xx < 0)  xx = xx + b;
        ll yy = (ll)(m - a * xx) / b;
        if(yy < 0)
            puts("-1");
        else
            printf("%lld %lld\n", xx, yy);
    }
}

\(Q3:\)关于正整数解的个数

根据通式算出上下界即可。

因为是正整数解那么:\(x^* = x_0+\dfrac{b}{(a,b)}t\ge 1\)

于是\(t\ge (1-x_0)\dfrac{(a,b)}{b}\)

又因为是整数,那么取整一下。同理另一边也是一样的道理。

\(\lceil(1-x_0)\dfrac{(a,b)}{b}\rceil \leq t \leq \lfloor(y_0-1)\dfrac{(a,b)}{a}\rceil\)

\(Q4:\)exgcd求出的特解的范围

对于\(ax+by=\gcd(a,b)\),当\(a,b\)都不等于\(0\)且\(a\neq b\)的情况下满足绝对值最小

在数值上有:\(|x|\le \dfrac{|b|}{2},|y|\le \dfrac{|a|}{2}\).

对于\(ax+by=c\)就是\(|x|\le |\dfrac{b}{2c}|,|y|\le |\dfrac{a}{2c}|\)

关于正解和非负解的几个结论:

由于\(ax+by=c\)我们可以转化成\(ax+by=d\),其中\((d=gcd(a,b))\)的形式去求解,那么下面只说明\((a,b)=1\)的情况:

1.非负整数解

设\(ax+by=c\),其中\(a,b,c\)均为正整数且\((a,b)=1\)。

  • 当\(c>ab-a-b\)时,有非负解,且解的个数为\(N_0 = \lfloor\dfrac{c}{ab}\rfloor\)或\(N_0 = \lfloor\dfrac{c}{ab}\rfloor+1\)
  • 当\(c=ab-a-b\)时,没有非负解。
  • 当\(c<ab-a-b\)时,恰有\(\dfrac{(a-1)(b-1)}{2}\)个正整数有非负解,具体是哪些不清楚。

2.正整数解

正整数解的个数为:\(N_1 = -\lfloor\dfrac{-c}{ab}\rfloor-1\)或者$ -\lfloor\dfrac{-c}{ab}\rfloor$

  • 当\(c>ab\)时有正解
  • 当\(c=ab\)时无解
  • 当\(c<ab\)时,恰有\(\dfrac{(a-1)(b-1)}{2}\)个正整数有非负解,具体是哪些不清楚。

推论:

  1. 设\((a,b)=1\)且\(a,b>0\)时,\(ab-a-b\)是不能表示成\(ax+by(x,y\ge 0)\)形式的最大整数。
  2. 设\((a,b)=1\)且\(a,b>0\)时,\(ab\)是不能表示成\(ax+by(x,y> 0)\)形式的最大整数。

显然如果\(x,y\)可以是负数,那可以表示为任意整数了。

​ 3. \(ax+by=c\)有解的充要条件是方程\(ax+by=c-a-c\)有非负解。

1.5*取模意义下的一元二次方程的最小值

起源是关于这个题中提取的思路。

\(ax+by\)能组成的最小正整数是\((a,b)\)。但是在取模意义下呢?

考虑以下两个问题:

  1. \((ax+by)\bmod m\)的最小值
  2. \((ax+by+t)\bmod m\)的最小值

先说结论

  1. \((ax+by)\bmod m\)的最小值是:\(\gcd(a,b,m)\)
  2. \((ax+by+t)\bmod m\)的最小值是\(t \bmod \gcd(a,b,m)\)

\(Pfoof.\)

①先讨论第一个:\((ax+by)\bmod m\)的最小值

\(ax+by=k_1\times \gcd(a,b) = k_1g_1\)

那么我们只要考虑\(k_1g_1 \bmod m\)意义下的最小值即可。

\(k_1g_1 \bmod m\)

设\(k_1g_1 ≡ r (\bmod m)\),利用同余性质可知\((k_1g_1-r)\)的\(m\)的倍数,于是\(k_1g_1 = k_2m+r\),移向得\(k_1g_1-k_2m = r\)。

tips:同余 设\(m\)是正整数,若\(a,b\)是整数,且\(m|(a-b)\),则称\(a\)和\(b\)模\(m\)同余。或者说\((a-b)\)除以\(m\)余数为\(0\)。

显然上面这个柿子已经没有模的意义了,那么上面这个\(r\)最小显然是\(\gcd(g_1,m)\)

那么\((ax+by)\bmod m\)的最小值是\(\gcd(g_1,m) = \gcd(a,b,m)\)

②那么对于\(ax+by+t\)呢?

\(ax+by+t = k_1g_1+t = k_2m+r\)

\(r = k_1g_1-k_2m+t = k\times\gcd(g1,m)+t\)

上面这个柿子的含义就是要保证\(t>0\)的前提下减去尽可能多的\(\gcd(g_1,m)\),那也就是\(r_{min} =t \mod \gcd(a,b,m)\)

有了上面的分析,下面来说这个题就很容易了。

题目是要求:\((sum+ax+by)\bmod m\)的最小值,并求出\(x,y\)。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
ll n,m;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0)
    {
        x = 1; y = 0;
        return a;
    }
    ll xx,yy;
    ll d = exgcd(b, a % b, xx, yy);
    x = yy;
    y = xx - (a / b) * yy;
    return d;
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    cin>>n>>m;
    ll sum = 0;
    for(int i = 1; i <= n; i++)
    {
    	ll x;
    	cin>>x,sum += x;
    }

    ll a = n,b = (n+1ll)*n/2,x,y;
    //ax+by=k1g1(mod m)
    ll g1 = exgcd(a,b,x,y);
    //k1g1+tm = k2g2
    ll k1,t;
    ll g2 = exgcd(g1,m,k1,t);
    //ans = min(sum + k2g2)
    ll ans = sum%g2;
    k1 *= ((ans-sum)/g2)%m;
    k1 %= m;
    x*=k1,y*=k1;
    cout<<ans<<"\n";
    cout<<(x%m+m)%m<<" "<<(y%m+m)%m<<"\n";
    return 0;
}

1.6 一些习题练习

I. Step

思路:由题意转化得:我们要求出\(\dfrac{k\times(1+k)}{2}\bmod lcm(p_1,p_2...p_n) = 0\)的最小的\(k\)。

设\(lcm(p_1,p_2...p_n) = LCM\),那么题目转化为\(k\times(1+k)≡0(\bmod 2\times LCM)\)

设\(t = 2\times LCM\)

即:$k\times (1+k)-m\times 2\times t = 0 $

问题转化为求:\(k\times(1+k) = 2\times t \times m\)的最小的\(k\),即求\(2t|k(k+1)\)的最小的\(k\)。不妨假设\(2t = A*B\),且\(A|k,B|(k+1)\),我们设\(Ax = k,By = k+1\)那么\(Ax +1= By\),移向得:\(-Ax+By = 1\),这里用\(exgcd\)得到最小的\(x\)。

显然\(A,B\)互质的,我们可以考虑对\(2t\)质因数分解,把不同种的质因数分配到\(A,B\)上(二进制枚举方案)即可,

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;

ll lcm(ll a,ll b)
{
    return a/__gcd(a,b)*b;
}
vector<ll>a;
map<ll,ll>mp;
void primer(ll x)
{
    for (ll i = 2; i <= x / i; i++)
    {
        if (x % i == 0)
        {
            int s = 0;
            a.push_back(i);
            mp[i] = 1;
            while (x % i == 0)
            {
                x = x / i;
                s++;
                mp[i]*=i;
            }
        }
    }
    if (x > 1)a.push_back(x),mp[x] = x;;
}

ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0)
    {
        x = 1; y = 0;
        return a;
    }
    ll xx,yy;
    ll d = exgcd(b, a % b, xx, yy);
    x = yy;
    y = xx - (a / b) * yy;
    return d;
}
 

ll p[N];
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int n;
    cin>>n;
    for(int i = 1;i <= n; i++)
        cin>>p[i];
    ll LCM = p[1];
    for(int i = 1;i <= n; i++)
        LCM = lcm(LCM,p[i]);
    ll t = LCM*2;
    primer(t);
    int sz = a.size();
    ll ans = 1e18;
    for(int i = 0;i < (1<<sz); i++)
    {
        ll A = 1,B,x,y;
        for(int j = 0;j < sz; j++)
        {
            if((i>>j)&1)A*=mp[a[j]];
        }
        B = t/A;
        ll d = exgcd(A,B,x,y);
        //ax+1=by
        //-ax+by = 1
        x = -x;
        x = (x%(B/d)+(B/d))%(B/d);
        if(A*x!=0)
        {
            ans = min(ans,A*x); 
        }
    }
    cout<<ans<<"\n";
    return 0;
}

A. Modulo Ruins the Legend

思路:什么讲解exgcd的时候又讲,主要思想就是,先去除取模的意义。什么意思呢?

就是说对于\(ax+by+sum \mod m\)后的最小值即\(min(ax+by+tm+sum)\)的最小值

即\(k2g2+sum\)的最小值。这样我们处理出没有取模意义的等式就可以计算啦。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
ll n,m,sum;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0)
    {
        x = 1; y = 0;
        return a;
    }
    ll xx,yy;
    ll d = exgcd(b, a % b, xx, yy);
    x = yy;
    y = xx - (a / b) * yy;
    return d;
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    cin>>n>>m;
    for(int i = 1;i <= n; i++)
    {
        ll x; cin>>x,sum += x;
    }
    ll a = n,b = (n+1)*n/2,x,y,k1,t;
    ll g1 = exgcd(a,b,x,y);
    ll g2 = exgcd(g1,m,k1,t);
    //ax+by = k1g1 (mod m)
    //k1g1+tm = k2g2
    //ans = min(sum + k2g2) = sum%g2
    //k2 = ((ans-sum)/g2)%m
    //k1*=k2
    //x*=k1,y*=k1;
    ll ans = sum%g2;
    cout<<ans<<"\n";
    ll k2 = (ans-sum)/g2%m;
    k1*=k2,k1%=m;
    x *= k1,y*=k1;
    cout<<(x%m+m)%m<<" "<<(y%m+m)%m<<"\n";


    return 0;
}

M-Water_“范式杯”2023牛客暑期多校训练营1

思路:不妨假设\(x = s_0A+r_0B\)

显然的\(s_0\ge0,r_0\ge0\)时,\(ans = 2*(s_0+r_0)\)

因为不可能同时为负数,那么当\(s_0,r_0\)异号的时候呢?

不妨假设\(s_0>0,r_0<0\)

对于以下的讨论不妨假设\(A>B\)且\(|s_0|>|r_0|\),于是:

\(x = s_0A+r_0B = (s_0+r_0-r_0)A+r_0B = (s_0+r_0)A+(-r_0)(A-B)\)

含义就是喝\(s_0+r_0\)杯\(A\),贡献是\(2*(s_0+r_0)\),再喝\(-r_0\)杯\(A-B\)水。

对于\(A-B\)需要分\(4\)步考虑:

  1. 将\(A\)倒满
  2. 将\(A\)中水导入\(B\),此时杯中:\(A-B,B\)的水
  3. 将\(A-B\)的水喝掉
  4. (*)将第二个杯子里面的\(B\)倒掉(如果是最后一步就不要这一步)

那么\(ans2 = 4(-r_0-1)+3\)

最后答案就是\(ans = ans1+ans2 = 2s_0-2r_0-1\)

接下来我们只需先求出一个特解,然后在特解附件去求它的通解就可以了。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0)
    {
        x = 1; y = 0;
        return a;
    }
    ll xx,yy;
    ll d = exgcd(b, a % b, xx, yy);
    x = yy;
    y = xx - (a / b) * yy;
    return d;
}
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--)
    {
        ll A,B,X,x,y;
        cin>>A>>B>>X;

        ll d = exgcd(A,B,x,y);
        if(X%d){
            cout<<"-1\n";
            continue;
        }
        else{
            //x = xxA+yyB
            //x = (xx+yy-yy)A+yyB
            //x = (xx+yy)A+(-yy)(A-B)
            //ans1 = 2*(xx+yy)
            //ans2 = 4*(-yy-1)+3
            //ans1+ans2 = 2xx+2yy-4yy-4+3 = 2xx-2yy-1
            A /= d;
            B /= d;
            X /= d;
            ll xx = (ll) (x % B) * (X % B) % B;
            ll yy = (ll)(X - A * xx) / B;
   
            ll ans = 1e18;
            for(int i = -10;i <= 10; i++)
            {
                ll nx = (xx + B*i),ny = (yy - A*i);
                if(nx>=0 && ny>=0)
                    ans = min(ans,2*(nx+ny));
                else ans = min(ans,2*abs(nx-ny)-1);
            }
            cout<<ans<<"\n";
            
            
        }
       
    }

    return 0;
}

Red-Black Pepper

思路:由于我们只能在一个店买,该商店红辣椒\(a_i\)份一包,黑辣椒\(b_i\)份一包,且只能买整包的。那么要求买到份数恰好是\(n\),那么就有\(a_ix+b_iy = n\),含义是在第\(i\)家店买\(x\)包红的和\(y\)包黑的总共\(n\)份。

第\(i\)道菜用了红辣椒可以增加\(a[i]\)的美味值,用黑的增加\(b[i]\)。

我们设\(f(x)\)为买\(x\)份红的可以达到的最大美味值。

我们先考虑一个简单的问题:买\(x\)份红的,\(y\)份黑的的最大美味值是多少?\((x+y=n)\)

让我们感性的去理解一下:假设我全买了黑的,再用贪心的策略把其中\(x\)份换成红的。

全买黑的贡献\(f(0) = \sum_{i = 1}^{n}b[i]\),对于第\(i\)道菜换成红的\(f(1) = f(0)-b[i]+a[i] = f(0)+(a[i]-b[i])\)

那么我们按照\((a[i]-b[i])\)从大到小排序,贪心的去选那\(x\)个红的。

我们观察\(a[i]-b[i]\)是单减的,可能由某一个(可能是正数)逐渐减小变成负数。

那么我们\(f(x)\)由一个先增后减的趋势,是个有极值的函数(开口向下)。

那么处理好了\(f(x)\),接下来考虑询问。

很显然\(ax+by=n\)的解用\(exgcd\)可解。

我们先求出来了\(ax+by=\gcd(a,b)\)的一个特解\(x_0,y_0\)

如果说\(n\bmod \gcd(a,b)\ne 0\),那就无解直接\(continue\)。

否则的话继续讨论。

特解知道了,我们可以进一步求出它们的通解:

\[\begin{cases} x^*=\quad x_0+t\times\dfrac{b}{\gcd(a,b)}\\ y^*=\quad y_0-t\times\dfrac{a}{\gcd(a,b)} \end{cases} \tag{1} \]

回归问题\(ax+by=n\)

\[\begin{cases} ax^*=\quad ax_0+a\times t\times\dfrac{b}{\gcd(a,b)} \\ by^*=\quad by_0-b\times t\times\dfrac{a}{\gcd(a,b)} \end{cases} \tag{2} \]

\[\begin{cases} ax^*=\quad ax_0+ t\times lcm(a,b) \\ by^*=\quad by_0- t\times lcm(a,b) \end{cases} \tag{3} \]

对于\(ax^* = ax_0+t[a,b] = ax_0+td\)

因为\(f(x)\)是有极值的单峰函数,我们可以暴力\(for\)一遍找到最大值和最大值\(maxv\)的横坐标\(high\)

对于通解:\(ax_0+td\)

显然\(t\)最小取\(0\),最大取\(\dfrac{(n-ax_0)}{d}\)

\(td = \dfrac{(n-ax_0)}{d}*d = (n-ax_0)-(n-ax_0)\% d\)

那么对于通解的最小值就是\(mi =ax_0\),最大值就是\(mx = ax_0+(n-ax_0)-(n-ax_0) \% d = n-(n-ax_0)\%d\)

然后我们在从任意集中,选择离\(high\)下标最近的两组,取两者的\(\max\)即可。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 3e5 + 10;
ll n,m,c[N],f[N],a[N],b[N];
bool cmp(int a,int b)
{
    return a>b;
}
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0)
    {
        x = 1; y = 0;
        return a;
    }
    ll xx,yy;
    ll d = exgcd(b, a % b, xx, yy);
    x = yy;
    y = xx - (a / b) * yy;
    return d;
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    cin>>n;
    for(int i = 1;i <= n; i++)
        cin>>a[i]>>b[i],c[i] = a[i]-b[i],f[0]+=b[i];
    sort(c+1,c+1+n,cmp);
    for(int i = 1;i <= n; i++)f[i] = f[i-1]+c[i];
    ll high = 0,maxv = f[0];
    
    for(int i = 1;i <= n; i++)
        if(f[i]>maxv)high = i,maxv = f[i];
    cin>>m;
    while(m--)
    {
        ll a,b,x,y;

        cin>>a>>b;
        ll g = exgcd(a,b,x,y);
        if(n%g){
            cout<<"-1\n";
            continue;
        }
        x = x*(n/g)%(b/g);
        if(x<0)x+=(b/g);
        if(a*x>n){
            cout<<"-1\n";
            continue;
        }
        /*
            x = x0+b/g*t
            y = y0-a/g*t

            ax+by = n

            ax = ax0+ab/g*t
            by = ay0-ab/g*t

            ax = ax0+dt
        */
        ll d = a/g*b;//lcm
        //ax+by = n
        //ax+t[a,b] = ax+td
        //ax+td = n
        //t = (n-ax)/d
        //t的最小值显然是0,最大值是(n-ax)/d
        ll mi = a*x,mx = n-(n-a*x)%d;
        ll ans = max(f[mi],f[mx]);
        if(high>=mi && high<=mx)
        {
            ll l = high-(high-mi)%d;//下取整
            ll r = high+(mx-high)%d;//上取整
            ans = max(ans,max(f[l],f[r]));
        }
        cout<<ans<<"\n";
    }
    return 0;
}

Two Arithmetic Progressions

题意:给定\(a_1,a_2,b_1,b_2,L,R\).要求找出\([L,R]\)中的\(x\)的数量,使得\(x\)满足\(x = a_1k_1+b_1 = a_2k_2+b_2\)。

思路:题目要求\(a_1k_1+b_1 = a_2k_2+b_2\)

我们移向得:\(a_1k_1-a_2k_2 = b_2-b_1\),写成我们常见写法就是\(a_1x-a_2y = b_2-b_1\)。

显然我们可以用\(exgcd\)直接判无解的情况\((b_2-b_1)\%\gcd(a1,g1)\neq 0\)。那么对于有解的情况呢?

首先因为参数有负数,那么我们可以知道,\(x,y\)是同增同减的。我们可以求得一组最小的\(x,y\)。怎么求呢?

我们可以直接通过\(exgcd\)求得一组特解\(x',y'\)。我们令\(\Delta x= \dfrac{a2}{\gcd(a_1,a_2)},\Delta y = \dfrac{a1}{\gcd(a,b)}\)

那么通解就是:

\[\begin{cases} x=\quad x'+k\times \Delta x\\ y=\quad y'+k\times\Delta y \end{cases} \tag{1} \]

又由于\(x,y\)是同增同减的,那么我们要找到一组最小的解,当且仅当:

\(x-\Delta x<0\)或\(y-\Delta y<0\)

我们令\(x'' = x' \% \Delta x,y'' = y'\%\Delta y\)

那么我们的通解就变成了:

\[\begin{cases} x=\quad x''+k\times \Delta x\\ y=\quad y''+k\times\Delta y \end{cases} \tag{2} \]

因为我们要求\(a_1k_1+b_1 = a_2k_2+b_2\)有多少属于\([L,R]\)的,那么我们把\(x\)代入\(a_1x+b_1\)得:\(a_1(x''+k\times \Delta x)+b_1\in[L,R]\)

那么可以取的\(k\)的个数就是我们的答案了。

即:\(k\in[\dfrac{L-a_1x''-b_1}{a_1\Delta x},\dfrac{R-a_1x''-b_1}{a_1\Delta x}]\)

接下来我们只需要求出\(k_{min}\)和\(k_{max}\)即可,答案就是\(max(0ll,k_{max}-k_{min}+1)\)

注意:取整要手写,因为当其是负数时候,c++默认往0取整,所以下取整我们要自己写

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0)
    {
        x = 1; y = 0;
        return a;
    }
    ll xx,yy;
    ll d = exgcd(b, a % b, xx, yy);
    x = yy;
    y = xx - (a / b) * yy;
    return d;
}

ll floordiv(ll a,ll b)
{
	//因为当其是负数时候,c++默认往0取整,所以下取整我们要自己写
	if(a%b==0)return a/b;
	else if(a>0)return a/b;
	else return a/b-1;
}

ll ceildiv(ll a,ll b)
{
	if(a%b==0)return a/b;
	else if(a>0)return a/b+1;
	else return a/b;
}

int main()
{
	ll a1,b1,a2,b2,l,r,x,y;
	cin>>a1>>b1>>a2>>b2>>l>>r;
	ll d = exgcd(a1,a2,x,y);
	y = -y;
	if((b2-b1)%d){
		cout<<"0\n";
		return 0;
	}
	x *= (b2-b1)/d,y *= (b2-b1)/d;
	ll dx = abs(a2/d),dy = abs(a1/d);
	x = (x%dx+dx)%dx,y = (a1*x-(b2-b1))/a2;
	if(y<0)y = (y%dy+dy)%dy,x = (a2*y+(b2-b1))/a1;
	ll kmin = max(0ll,ceildiv(l-a1*x-b1,a1*dx));
	ll kmax = floordiv(r-a1*x-b1,a1*dx);
	cout<<max(0ll,kmax-kmin+1)<<"\n";
	return 0;
}

标签:总结,int,dfrac,ll,exgcd,yy,关于,ax
From: https://www.cnblogs.com/nannandbk/p/17677885.html

相关文章

  • 关于synchronized
    关于synchronizedsynchronized是java中的关键字,可以在需要线程安全的业务场景中进行使用,保证线程安全,它是利用锁机制来实现同步的。synchronized锁对象和锁类对象锁:每个实例都会有一个monitor对象,即Java对象的锁,类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰......
  • 实习总结:踩过的一些坑
    目前接了几个需求,但每次写代码一时爽,debug火葬场,因此总结了一些我自己常常犯的错误:1、对于json的解析需要写之前明确每一层的类型和层级的嵌套关系。2、对于es如果需要获取10个以上检索效果,需要更改size。3、对于chanel如果采用range去读,需要保证其已经关闭。4、对于无法读库......
  • [ 总结 ] Linux 下文件描述符
    1、概述:文件描述符是内核为了高效管理已被打开的文件所创建的索引。是一个非负整数,用于代指被打开的文件。所有通过I/O操作的系统调用都通过文件描述符。文件描述符用以表明每一个被进程所打开的文件和socket 2、文件描述符的限制:Linux下最大文件描述符的限制......
  • [ 总结 ] Linux系统测试硬盘I/O
    检测硬盘I/O相对来说还是一个比较抽象的概念,但是对系统性能的影响还是至关重要的。(1)使用hdparm命令检测读取速度:   hdparm命令提供了一个命令行的接口用于读取和设置IDE和SCSI硬盘参数。   安装:      yuminstallhdparm   语法:      hdparm(选项......
  • log4j结合commons-logging配置总结
    作者fbysss关键字:loggingcommons-logging是一个通用的日志接口,commons-logging.jar包中自带了一个simplelog的实现log4j也实现了这个接口使用通用接口,方便在于如果更换实现的方式,只要修改一个配置项即可配置过程:commons-logging.properties必须放置在WEB-INF/classes/下面log4j.pro......
  • MySQL修改密码方法总结
    MySQL修改密码方法总结作者:intphp<scripttype=text/javascript></script><scriptsrc="http://pagead2.googlesyndication.com/pagead/show_ads.js"type=text/javascript></script><scriptsrc="http://down.meety.com/asrep/......
  • Dotnet6 NPOI操作Excel基本操作总结
    背景需要对Excel进行读取和写入,目前使用Dotnet6开发环境,故直接使用。达到的效果:兼容.xls和.xlsx,识别行为空自动跳过,识别显示值,识别格式内容步骤Dotnet6Nuget安装NPOI,具体版本2.6.1,tips:搜索资料时,可能NPOI1与NPOI2可能有出入。使用方法获取相应文档对象......
  • 关于使用new Integer还是Integer.valueOf的研究
    作者:fbysss前言:最近看到这样的说法:使用Integer.valueOf代替newInteger更有效率,原因是研究了Integer源码,发现有一个缓存可以利用。对此我也一探究竟。发现这其实与Java的自动装箱拆箱有关,直接使用Integeri=数值的方式即可。通过字节码研究是比较有效的方式。那我们来看看吧:-----......
  • 关于NFC、谷歌钱包的理解
    NFC:NearFieldCommunication即近距离通讯技术RFID(RadioFrequencyIDentification)技术,又称电子标签、无线射频识别,公交卡就是使用这种技术。NFC传输范围较小,安全、低功耗RFID是一种识别技术,而NFC是一种交互的通信方式。有人说,NFC是RFID的一种改良版本。目前智能手机将支持NFC,也......
  • 关于JVM的server/client版本
     区别:Client启动快些,Server版进行了优化,运行会快一些,但启动会慢些。查看当前JVM版本:java-version位置:jre/bin/server  jre/bin/client两个jvm文件大小都不一样。如果没有指定JVM版本,会自动根据OS和硬件环境进行识别。windows下默认是client,Unix下,会进行Server-ClassMachine......