二次剩余

//二次剩余  
//一般是求 x^2=n(mod p) 的解的,其中一般是保证p为奇素数,但是这套板子还处理了2的情况,把素数的情况大部分都包含了
# include <bits/stdc++.h>
using namespace std;

typedef long long LL;
# define random(a,b) (rand()%(b-a+1)+a)
/*
LL mul(LL a,LL b,LL mod)//防止爆LL,会减慢速度
{
    LL res=0;
    while(b)
    {
        if(b&1LL) res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}
*/
LL quick_pow(LL a,LL b,LL mod)
{
    LL ret=1;
    while(b)
    {
        if(b&1LL) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}
LL p;
LL w;//二次域的D值
bool ok;//是否有解
struct QuadraticField//二次域
{
    LL x,y;
    QuadraticField operator*(QuadraticField T)//二次域乘法重载
    {
        QuadraticField ans;
        ans.x=(this->x*T.x%p+this->y*T.y%p*w%p)%p;
        ans.y=(this->x*T.y%p+this->y*T.x%p)%p;
        return ans;
    }
    QuadraticField operator^(LL b)//二次域快速幂
    {
        QuadraticField ans;
        QuadraticField a=*this;
        ans.x=1;
        ans.y=0;
        while(b)
        {
            if(b&1LL)
            {
                ans=ans*a;
                b--;
            }
            b/=2;
            a=a*a;
        }
        return ans;
    }
};
LL Legender(LL a)//求勒让德符号 (a p)=a^((p-1)/2)  (mod p)
{
    LL ans=quick_pow(a,(p-1)/2,p);
    if((ans+1)==p) return -1;//如果ans的值为p-1,那么%p之后会变成-1
    else return ans;
}
LL Getw(LL n,LL a)//根据随机出来的a值确定对应w的值
{
    return ((a*a-n)%p+p)%p;//防爆处理
}
LL Solve(LL n)
{
    LL a;
    if(p==2) return n;//当p为2的时候,n只会是0或1,然后0和1就是对应的解
    if(Legender(n)==-1) ok=false;//无解
    srand((unsigned)time(NULL));
    while(1)//随机a,直到a为二次非剩余
    {
        a=random(0,p-1);
        w=Getw(n,a);
        if(Legender(w)==-1) break;
    }
    QuadraticField ans,res;
    res.x=a;
    res.y=1;//res的值就是a+sqrt(w)
    ans=res^((p+1)/2);
    return ans.x;
}
int main()
{
    int T;
    scanf("%d",&T);
    LL n,ans1,ans2;
    while(T--)
    {
        scanf("%lld%lld",&n,&p);
        if(n==0){
            printf("0\n");
            continue;
        }
        ok=true;
        n%=p;
        ans1=Solve(n);//计算出其中的一个解x
        ans2=p-ans1;//另外一个解就是p-x
        if(!ok){//无解
            printf("Hola!\n");
            continue;
        }
        //输出的解按照升序排列
        if(ans1>ans2) swap(ans1,ans2);
        if(ans1==ans2) printf("%lld\n",ans1);
        else printf("%lld %lld\n",ans1,ans2);
    }
    return 0;
}

posted @ 2022-02-26 23:13  fengzlj  阅读(43)  评论(0)    收藏  举报