题意:一只青蛙在坐标轴的原点,它每次可以向左或向右跳x1或x2的距离,现在,它想跳K次恰好跳到P点,求一个跳的方案,P1为向右跳x1的次数,N1为向左跳x1的次数,P2为向右条x2的次数,P3为向左跳x2的次数。
思路:唉,对数论什么的一直没感觉啊,做了半天好不容易做出来了……首先是求一个可行解,这个简单,相当于解ax+by=c的方程,直接用扩展欧几里得就可以搞定,这里求出的x和y分别相当于P1-N1,P2-N2,剩下是怎么安排次数的问题……首先要把abs(x)+abs(y)的值变到最小,我们知道对于扩展欧几里得求出的解x0,y0来说,x=x0+k*(y0/d) y=y0-k*(x0/d)都是方程的解那么我们就可以通过这个来调整,令abs(x)+abs(y)的值最小,如果此时abs(x)+abs(y)还是大于k,那么说明无解。否则,看此时还需要走多少歩(也就是k-abs(x)+abs(y)),如果是偶数歩,那么简单,剩下的步数只要不断重复走P1,N1就行了。如果不行的话……我们知道通过上面寻找最小值的方法可以调整x和y的值,此时,如果(y0/d)和(x0/d)的奇偶性不同,那么分别对x和y增加(减少)这两个数就可以改变奇偶性了,否则就无解……
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
void ext_gcd(ll a,ll b,ll & x,ll & y,ll & d)
{
if(b==0)
{
d=a;x=1;y=0;
return ;
}
ext_gcd(b,a%b,y,x,d);
y-=(a/b)*x;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
ll x,y,p,k,d,tmp;
ll N1,N2,P1,P2,ta,tb;
cin>>x>>y>>p>>k;
if(p==0&&k==0)
{
cout<<"YES"<<endl;
cout<<"0 0 0 0"<<endl;
return 0;
}
ext_gcd(x,y,P1,P2,d);
if(p%d) {cout<<"NO"<<endl;return 0;}
ta=x/d;tb=y/d;
P1=P1*(p/d);
P2=P2*(p/d);
while(abs(P1)+abs(P2)>abs(P1-tb)+abs(P2+ta)) {P1-=tb;P2+=ta;}
while(abs(P1)+abs(P2)>abs(P1+tb)+abs(P2-ta)) {P1+=tb;P2-=ta;}
if(abs(P1)+abs(P2)>k)
{
cout<<"NO"<<endl;
return 0;
}
N1=N2=0;
tmp=k-abs(P1)-abs(P2);
if(tmp%2==0)
{
if(P1<0) swap(P1,N1),N1=-N1;
if(P2<0) swap(P2,N2),N2=-N2;
P1+=(tmp/2);
N1+=(tmp/2);
}
else
{
if((ta+tb)%2==0)
{
printf("NO\n");
return 0;
}
else
{
if(abs(P1+tb)+abs(P2-ta)<abs(P1-tb)+abs(P2+ta))
{
P1+=tb;
P2-=ta;
}
else
{
P1-=tb;
P2+=ta;
}
if(abs(P1)+abs(P2)>k)
{
printf("NO\n");
return 0;
}
k-=abs(P1)+abs(P2);
if(P1<0) swap(P1,N1),N1=-N1;
if(P2<0) swap(P2,N2),N2=-N2;
P1+=(k/2);
N1+=(k/2);
}
}
cout<<"YES"<<endl;
cout<<P1<<" "<<N1<<" "<<P2<<" "<<N2<<endl;
return 0;
}