解二次剩余方程 q2≡a(modp):
百度词条:二次剩余
当存在某个式子成立时,称“d是模p的二次剩余”
一.判断二次剩余方程是否有解。
欧拉准则:(判断二次剩余方程是否有解)
若是p奇质数且不能整除d,则:
d是模p的二次剩余当且仅当:
是d模p的非二次剩余当且仅当:
以勒让德符号表示,即为:
二.通过已知条件和欧拉准则化简
已知条件:
易得 (x−y)2≡(x+y)2−4xy (mod p)⋯⋯⋯(1)
将条件
带入上式得:
(x−y)2≡b2−4c (mod p)⋯⋯⋯(2)
首先,判断 该二次剩余方程式(2)是否由解,即判断 b2−4c 是否为模 p 的二次剩余;
设d=b2−4c;
通过欧拉准则:如果d(p-1)/2≡1 (mod p):
那么,易得 (x−y)2≡d(p+1)/2 (mod p)⋯⋯⋯(3);(d≡d⋅d(p−1)/2 (mod p))
同余式两端同时开方得:(x−y)≡d(p+1)/4 (mod p)⋯⋯⋯(4);
由条件 和式 (4) 可得:
所以,式(5)可以解出x;(用扩展欧几里得求ax+by=c)
那么
由条件可得 x+y = b or b+p;
那么,y 也就求出来了;
思路参考于https://www.cnblogs.com/violet-acmer/p/11364835.html
AC代码;
#include<bits/stdc++.h>
using namespace std;
#define ll long long
//typedef __int128 ll;
ll p=1000000007;
ll exgcd(ll a,ll b,ll &x,ll &y)//扩展欧几里得算法 注意取址符不能少
{
if(b==0)
{
x=1;y=0;
return a;
}
ll r=exgcd(b,a%b,x,y);
ll temp=y;
y=x-(a/b)*y;
x=temp;
return r;
}
ll Quick_pow(ll a,ll b)//快速幂并求余
{
ll ans=1ll;
while(b)
{
if(b&1)
ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
void solve(ll b,ll c)
{
ll d=b*b-4*c;
d=(d+p)%p;
if(d==0)//特判 此时x=y //这里容易坑人
{
printf("%lld %lld\n",b/2,b/2);//因为(x+y)mod p=b;故x=y=b/2;
return;
}
if(Quick_pow(d,(p-1)/2)!=1)//欧拉准则
{
printf("-1 -1\n");//无解
return;
}
ll tx,ty;
ll D=(b%p+Quick_pow(d,(p+1)/4))%p;
ll gcd=exgcd(2,p,tx,ty);
if(D%gcd!=0) return;//由贝祖公式可知无解,本题不用判
//2x+py=gcd(2,p)的x已经求出为tx;但是我们要求的是方程2x+py=b+d^((p+1)/4)
//故 方程2x+py=b+d^((p+1)/4) 的解为
//x=tx/gcd*c; 但x必须为正数
ll g=p/gcd;
ll x= (tx/gcd*D%g+g)%g;
//printf("tx:%lld\n",tx);
ll y=b-x;
y=(y+p)%p;//保证正数
if(x>y)
swap(x,y);
printf("%lld %lld\n",x,y);//x<=y
return;
}
int main(void)
{
int t;
ll b,c;
scanf("%d",&t);
while(t--)
{
scanf("%lld %lld",&b,&c);
solve(b,c);
}
}
补充:
ax ≡ 1 (mod b)
可以写成
ax+by=1
运用扩展欧几里得算法,求出一组解。根据所有解的规律:
x加上b/gcd(a,b),同时y减去a/gcd(a,b)后,仍满足ax+by=c。
求出x的最小正整数解。
其实直接mod b也行的,就是有时候可能会溢出范围(这个个人觉得好理解点),所以最后mod a/gcd(a,b);