LINK:斩杀线计算大师
给出a,b,c三个值 求出 ax+by+cz=k的x,y,z的正整数解 保证一定有解。
考虑两个数的时候 ax+by=k 扩展欧几里得可以解决。
三个数的时候 一个暴力的想法暴力枚举c的系数z 然后进行计算扩欧 期望复杂度是过不了的 但是数据保证有解那么就很容易通过了。
考虑 (a,b)|k-cz 设g=(a,b) Tg=k-cz 对于这个式子 我们发现z越小那么tg越大 那么a和b更容易是正整数 且g|k-sz是ax+by=k-cz的必要条件。
所以我们解出 Tg=k-cz这个式子的答案并且让z尽可能的小 可以发现此时一定有解 且构造出的解cz一定比其他cz更优于满足答案。
所以两次扩展欧几里得解决。
const ll MAXN=1010;
ll a,b,c,k;
ll x,y;
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline ll exgcd(ll a,ll b)
{
if(!b){x=1;y=0;return a;}
ll w=exgcd(b,a%b);
ll z=x;x=y;y=z-a/b*y;
return w;
}
signed main()
{
freopen("1.in","r",stdin);
get(a);get(b);get(c);get(k);
ll g=gcd(a,b);
ll gg=exgcd(g,c);
ll s=k/gg;
y=y*s;x=x*s;
ll w1=g/gg;
ll w2=c/gg;
if(y<0)
{
ll sum=(-y)%w1==0?(-y)/w1:(-y)/w1+1;
y=y+sum*w1;
x=x-sum*w2;
}
ll sum=y/w1;
y-=sum*w1;
x+=sum*w2;
ll cc=y;
k=x*g;
gg=exgcd(a,b);
s=k/gg;
y=y*s;x=x*s;
w1=a/gg;w2=b/gg;
if(x<0)
{
ll sum=(-x)%w2==0?(-x)/w2:(-x)/w2+1;
x=x+sum*w2;
y=y-sum*w1;
}
if(y<0)
{
ll sum=(-y)%w1==0?(-y)/w1:(-y)/w1+1;
x=x-sum*w2;
y=y+sum*w1;
}
printf("%lld %lld %lld
",x,y,cc);
return 0;
}
虽然正解比暴力长很多 但是打完之后感觉对扩展欧几里得的理解更深了。
以后写的话不会很迷了。
果然做难题是搞定知识的重要途径。