P1082 [NOIP2012 提高组] 同余方程

128 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情

题意:求关于x的同余方程 ax≡1(modb) 的最小正整数解。 思路:先看一下扩展欧几里得算法,常用于求ax+by=gcd(a,b)的一组可行解。
过程:设ax1+by1=gcd(a,b),bx2+(a mod b)y2=gcd(b,a mod b)ax_1+by_1=gcd(a,b),bx_2+(a\ mod\ b)y_2=gcd(b,a\ mod\ b)
有欧几里得定理可知:gcd(a,b)=gcd(b,a mod b)gcd(a,b)=gcd(b,a\ mod\ b)
所以ax1+by1=bx2+(a mod b)y2ax_1+by_1=bx_2+(a\ mod\ b)y_2
又因为 a mod b=a(abb)a\ mod\ b=a-(\lfloor{\frac{a}{b}} \rfloor *b)
所以ax1+by1=bx2+(a(abb))y2ax_1+by_1=bx_2+(a-(\lfloor \frac{a}{b}\rfloor *b))y_2
ax1+by1=ay2+bx2abby2=ay2+b(x2aby2)ax_1+by_1=ay_2+bx_2-\lfloor{\frac{a}{b}} \rfloor*by_2=ay_2+b(x_2-\lfloor{\frac{a}{b}} \rfloor y_2)
因为a=a,b=ba=a,b=b,所以x1=y2,y1=x2aby2x_1=y_2,y_1=x_2-\lfloor{\frac{a}{b}} \rfloor y_2
将x2,y2不断带入递归求解直至gcd为0递归x=1,y=0回去求解
假如我们要将x扩大,即变成x+p,我们需要将y缩小,需要变成y-q.

a(x+p)+b(yq)=ca*(x+p)+b*(y-q)=c
ax+by=ca*x+b*y=c

联立解得p=bqap=\frac{b*q}{a},我们需要获得尽可能小的正整数p,q.那么就有abq,bbqa|b*q,b|b*q,即bqb*q最小值为lcm(a,b)=abgcd(a,b)lcm(a,b)=\frac{a*b}{gcd(a,b)},得出p=bd,q=adp=\frac{b}{d},q=\frac{a}{d},如果x偏大就需要求一个k使得x+kp>=1,k>1xpx+k*p>=1,k>\lceil \frac{1-x}{p} \rceil,接着我么们就可以x+=pk,y+=qkx+=p*k,y+=q*k,最后x就是最小的正整数
【题解】【P5656-【模板】二元一次不定方程(exgcd)】 - 古明地觉世界第一! - 洛谷博客 (luogu.com.cn)

#include<bits/stdc++.h>
//#pragma-GCC-optimize("-Ofast");
#define ll long long
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const ll mod=998244353;
const ll inf=1e18;
const double pi=acos(-1);
const int N=1e6+100;
ll qpow(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll getinv(ll a){return qpow(a,mod-2);}
int exgcd(int a,int b,int &x,int &y){
    if(b==0){
        x=1;y=0;
        return a;
    }
    int r=exgcd(b,a%b,x,y);
    int temp=y;
    y=x-(a/b)*y;
    x=temp;
    return r;
}
signed main()
{
    //ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int a,b,c=1;
    cin>>a>>b;
    int g=__gcd(a,b);
    int x,y,p=b/g,q=a/g,k;
    exgcd(a,b,x,y);
    x*=c/g;y*=c/g;
    if(x<0) k=ceil((1.0-x)/p),x+=p*k,y-=q*k;
    else if(x>=0) k=(x-1)/p,x-=p*k,y+=q*k;
    cout<<x<<endl;
    return 0;
}