【51Nod 1479】小Y的数论题

Description

小Y喜欢研究数论,并且喜欢提一些奇怪的问题。
这天他找了三个两两互质的数a, b, c,以及另一个数m, 现在他希望找到三个(0, m)范围内的整数x, y, z,使得
(xa+yb)modm=(zc)modm

Solution

这道构造题,好机智啊!

往扩展欧几里得想

因为存在很多解,我们看看存不存在以2为底的解。
因为 2a+2a=2a+1 ,所以 2abk+2abk=2abk+1
所以设 x=2bk y=2ak ,存在 xa+yb=2abk+1
因为有 xa+yb=zc ,所以mod m意义下, abk+1=lc ,那么就有 lcabk=1 ,所以可以用扩展gcd求出可行解,但是可能求出来的l或k是小于0的,所以需要找到都大于等于0的解。
最后成功!
机制的方法……

需要注意一种情况

如果m是2的幂数,那么就可以特判一下直接输出答案。
当a>1时x=m/2,y=z=1
否则当b>1时,类似
再否则当c>1时,x=y=z=m/2
若a=b=c=1,x=y=1,z=2

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
ll i,j,k,l,t,n,m,ans,q,a,b,c,x,y,z;
bool bz;
void exgcd(ll a,ll b,ll &x,ll &y){
    if (!b){
        x=1;y=0;
        return;
    }
    ll xx,yy;
    exgcd(b,a%b,xx,yy);
    x=yy;y=xx-a/b*yy;   
}
ll qsm(ll x,ll y){
    ll z=1;
    while(y){
        if(y&1)z=z*x%m;
        x=x*x%m;
        y/=2;
    }
    return z;
}
int main(){
    for(scanf("%lld",&q);q;q--){
        scanf("%lld%lld%lld%lld",&m,&a,&b,&c);
        j=m;bz=1;
        while(j){
            if(j==2)break;
            if(!(j%2))j/=2;
            else{
                bz=0;
                break;    
            }
        }
        if(!bz){
            ll l,k;
            exgcd(c,a*b,l,k);k=-k;
            while(k<0||l<0)k+=c,l+=a*b;
            x=qsm(2,b*k);y=qsm(2,a*k);z=qsm(2,l);
            printf("%lld %lld %lld\n",x,y,z);
        }
        else{
            if(a>1){
                printf("%lld 1 1\n",m/2);
            }
            else if(b>1){
                printf("1 %lld 1\n",m/2);
            }
            else if(c>1){
                printf("%lld %lld %lld\n",m/2,m/2,m/2);
            }
            else if(a==b==c){
                printf("1 1 2\n");    
            }
        }
    }
}

你可能感兴趣的:(扩展欧几里得,数论,51Nod)