题解 P5656 【【模板】二元一次不定方程(exgcd)】
先说两句:
- 本题解学习了编程&
\LaTeX 神仙\color{black}{\text{离}}\color{red}\text{散小波变换\degree} 的思路,让我们一起%ta!(管理员:题解抄袭题解,棕了!) - 在阅读本文之前,如果你已经学会了exgcd的基础用法,并且AC P1082 同余方程,那就更好啦!
没有做到也没关系,让我们先来了解一下exgcd的基础用法:
exgcd算法,听名字就知道跟辗转相除法求
exgcd:求
设
则
我们设已经找到了一组解
由于商
给它稍微变个形:
和原式对比一下,你会惊奇地发现:
由于
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
ll x,y;
void exgcd(ll a,ll b){
if(b==0){
x=1;
y=0;
return;
}
exgcd(b,a%b);
ll t=x;
x=y;
y=t-a/b*y;
}
int main(){
ll a,b;
cin>>a>>b;
exgcd(a,b);
cout<<(x%b+b)%b;
}
以上就是最基础的exgcd啦!但是我们肯定不能只满足于这些,接下来
正 片 开 始
……题目看样子好难鸭……
没关系,咱们一个一个来看!
若该方程无整数解,输出
-1
这个很好办,根据裴蜀定理,如果
(不了解裴蜀定理的小伙伴戳这,本文有很多地方也cv自这篇文章)
接下来,用exgcd求出
把大象关进冰箱里要分三步,那么要解一个二元一次不定方程要分几步呢?也是三步
首先,咱们来想这样一个问题,
step 1.用
ax+by=\gcd(a,b) 的整数解来求ax+by=c 的整数解
这就非常简单了,咱还是设
由于
从刚才的得到的结论出发,我们设已知
两边同时乘
step 2.用这组解
x_0,y_0 找出方程所有的解
咱们还是先从这组解
这样的话我们只需要求出
从这个式子出发:
展开它:
那只要让
继续设
带进去算一下
任取一个
step 3:考虑最大/最小值
这一步是重难点,竖起耳朵认真听哟!
首先,
可以看出,当
由于增加/减少的值太难写了,接下来设
接下来,我们需要找出
那到底减多少呢?
既然是正整数,那就要求
接下来就是解一元一次不等式啦!
两边同时除以
因为这边必须大于这个值,所以要取上整
这样的话,我们就可以得到
通过刚才的分析呢,
(没有整数解的话,我们还需要把
搞定了两个解,接下来咱们在搞剩下的两个:
先来看
有没有发现一个神奇的事情?我们要求的
那我们的
最后还剩下一个解的个数,还是看
总结一下就是这样的:
放码过来呀!
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;//十年OI一场空,不开long long见祖宗
ll x,y;
inline ll read(){//卡常卡得好,快读不能少
char c=getchar();
int f=1;
ll x=0;
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^'0');
c=getchar();
}
return x*f;
}
inline ll exgcd(ll a,ll b){//来一份萌萌哒exgcd
if(b==0){
x=1;
y=0;
return a;
}
ll d=exgcd(b,a%b);
ll t=x;
x=y;
y=t-a/b*y;
return d;//顺便把最大公因数也给求出来了呢!
}
int main(){
ll t=read();
while(t--){
ll a=read(),b=read(),c=read();
ll d=exgcd(a,b);//求出d
if(c%d){//不满足裴蜀定理,直接输出-1
printf("-1\n");
continue;
}
x=x*c/d,y=y*c/d;//求出x0和y0,tx和ty
ll tx=b/d,ty=a/d;
ll k=ceil((1.0-x)/tx);//求出k
x+=tx*k;
y-=ty*k;
if(y<=0){//如果没有正整数解,算出ymin之后输出
ll ymin=y+ty*1ll*ceil((1.0-y)/ty);
printf("%lld %lld\n",x,ymin);
}
else{//按照刚才说的处理
printf("%lld ",(y-1)/ty+1);
printf("%lld ",x);
printf("%lld ",(y-1)%ty+1);
printf("%lld ",x+(y-1)/ty*tx);
printf("%lld\n",y);
}
}
return 0;
}
The end