又用到了擴展歐幾里得,這題其實主要是手推公式... 草稿紙被我扔進垃圾桶N次,又被我撿回來N次...T_T
首先發現 (p1-n1)x1+(p2-n2)x2 =p,又是整數解,聯想擴展歐幾里得
p1-n1=s
p2-n2=t
p1+n1+p2+n2=k
p1>=0,p2>=0,n1>=0,n2>=0
進一步推得
2*(n1+n2)=k-s-t
n1>=max(0,-s)
n2>=max(0,-t)
再推
2*(n1+n2)>=2*(max(0,-s)+max(0,-t))
2*(n1+n2)=k-s-t
問題轉換爲判斷是否存在n1,n2滿足上面兩個式子,也就是判斷是否存在s,t 使得 k-s-t>=2*(max(0,-s)+max(0,-t))
看起來好像還是很複雜... 繼續推一下,討論s,t的正負。
討論過後發現問題變成了找s,t 使得 k>=fabs(s)+fabs(t)
我們利用擴展歐幾里得已經求得了兩個特解s0,t0 而s=s0+k'*x2/gcd(x1,x2) ;t=t0-k'*x1/gcd(x1,x2),其實直接讓我求最值我沒想出來怎麼做...但是我們可以枚舉k',從-40000到40000枚舉一遍,求得最值(這...全憑既視感= =|||),找最值的過程注意還要滿足k-s-t是2的倍數這個條件。
找到最值後,判斷一下k>=fabs(s)+fabs(t)即可。
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
void ex_gcd(LL a,LL b,LL &x,LL &y,LL &d)
{
if(b==0)
{
x=1,y=0,d=a;
return ;
}
else
{
ex_gcd(b,a%b,x,y,d);
int t=x;
x=y;
y=t-a/b*y;
return ;
}
}
int main()
{
LL x1,x2,p,k,p1,n1,p2,n2,s,t,d,tt1,tt2,m,fs,ft,i;
bool find;
scanf("%lld %lld %lld %lld",&x1,&x2,&p,&k);
ex_gcd(x1,x2,s,t,d);
if(p%d!=0)
{
printf("NO\n");
return 0;
}
s*=p/d,t*=p/d;
tt1=x2/d,tt2=x1/d;
find=false;
s-=40000*tt1;t+=40000*tt2;
for(i=-40000;i<=40000;++i)
{
if((k-s-t)%2==0&&(!find||fabs(s)+fabs(t)<m))
{
m=fabs(s)+fabs(t);
fs=s;
ft=t;
find=true;
}
s+=tt1,t-=tt2;
}
if(find&&k>=m)
{
printf("YES\n");
n1=max(0LL,-fs);n2=(k-fs-ft-2*n1)/2;p1=fs+n1;p2=ft+n2;
printf("%lld %lld %lld %lld\n",p1,n1,p2,n2);
}
else
{
printf("NO\n");
}
return 0;
}