SGU 141.Jumping Joe 数论,拓展欧几里得,二元不等式 难度:3

141. Jumping Joe

time limit per test: 0.25 sec. 
memory limit per test: 4096 KB

 

Joe is a frog who likes to jump a lot. In fact, that's all he does: he jumps forwards and backwards on the integer axis (a straight line on which all the integer numbers, both positive and negative are marked). At first, Joe sits next to the point marked with 0. From here, he can jump in the positive or the negative direction a distance equal to either x1 or x2. From the point where he arrived, he can jump again a distance equal to x1 or x2, in the positive or the negative direction and so on.. Joe wants to arrive next to the point marked with the number P, after exactly K jumps. You have to decide whether such a thing is possible.

 

Input

The input will contain four integers: x1x2 (0 < x1 , x2 < 40 000), P (-40 000 < P  < 40 000) and (0 <= K < 2 000 000 000), separated by blanks.

 

Output

The first line of output will contain the word "YES", in case Joe can reach the point marked with P after exactly K jumps, or "NO", otherwise. In case the answer is "YES", the next line should contain four integers, separated by blanks: P1 N1 P2 and N2P1 is the number of times Joe jumped in the positive direction a distance equal to x1N1 is the number of times Joe jumped in the negative direction a distance equal to x1P2 is the number of times Joe jumped in the positive direction a distance equal to x2N2 is the number of times Joe jumped in the negative direction a distance equal to x2. In other words, you should find four non-negative integers, so that:

P1*x1 - N1*x1 + P2*x2 - N2*x2 = P 
P1 + N1 + P2 + N2 = K

In case there are more quadruples (P1,N1,P2,N2) which are solutions for the problem, you may print any of them.

 

Sample Input

2 3 -1 12

Sample Output

YES
1 0 5 6

这题第一眼觉得是拓展欧几里得,设pn0=p0-n0,pn1=p1-n1,则可以通过拓展欧几里得得到一组满足等式1 的解,但是怎么得到四个答案数字都非负这一点卡了好久
明显,数据之间相差越小越优,且n0>=max(-pn0,0),n1>=abs(-pn1,0),p0>=max(pn0,0),p1>=abs(pn1,0)
则p0+p1+n0+n1=k>=(max(pn0,0)+max(-pn0,0)+max(pn1,0)+max(-pn1,0)),也就是说abs(pn1)+abs(pb0)<=k
又因为已经求出了一组pn1*x1+pn0*x0=pp,所以总有某个t使得(pn1-x0/gcd*t)*x1+(pn0-x1/gcd*t)*x0=pp
这里题解使用了暴力,从-40000求到40000
当确定一对pn0,pn1的时候,若从n0开始推起,那么n0=max(0,-pn0)(后面的是pn0为负数的时候p0为0,有问题的是第一次用pn0或pn1推出另一个数字的步骤,可能会导致负数,先确保这一步骤),n0+n1=(k-pn0-pn1)/2,可以解出这四个数字

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll extgcd(ll a,ll b,ll & x,ll &y){
    if(b==0){
        y=0;
        x=1;
        return a;
    }
    int t=extgcd(b,a%b,y,x);
    y-=a/b*x;
    return t;
}
ll x[2],p[2],n[2],pp,k,pn[2];
int main(){
    scanf("%I64d%I64d%I64d%I64d",x+0,x+1,&pp,&k);
    ll gcd=extgcd(x[0],x[1],pn[0],pn[1]);
    if(pp%gcd!=0){puts("NO");return 0;}
    ll r=pp/gcd;
    pn[0]*=r;pn[1]*=r;
    ll t[2];
    t[0]=x[1]/gcd;t[1]=x[0]/gcd;
    pn[0]-=40000*t[0];pn[1]+=40000*t[1];
    ll temp[2],sum=abs(pn[0])+abs(pn[1]);
    for(int i=-40000;i<=40000;i++){
        if((k+pn[0]+pn[1])%2==0&&(abs(pn[0])+abs(pn[1]))<sum){
            sum=abs(pn[0])+abs(pn[1]);
            temp[0]=pn[0];
            temp[1]=pn[1];
        }
        pn[0]+=t[0];pn[1]-=t[1];
    }
    pn[1]=temp[1];pn[0]=temp[0];
    if(sum>k){puts("NO");return 0;}
    n[0]=max(0LL,-pn[0]);
    p[0]=pn[0]+n[0];
    p[1]=(pn[0]+pn[1]+k)/2-p[0];
    n[1]=p[1]-pn[1];
   {printf("YES
%I64d %I64d %I64d %I64d
",p[0],n[0],p[1],n[1]);}
    return 0;
}

  

原文地址:https://www.cnblogs.com/xuesu/p/4064292.html