题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1132
以下来自:http://blog.csdn.net/acdreamers/article/details/10182281
今天要讨论的问题是解方程,其中是奇质数。
引理:
证明:由费马小定理,
引理:方程有解当且仅当
定理:设满足不是模的二次剩余,即无解,那么是二次
剩余方程的解。
证明:由,前面的等号用二项式定理和,后面的等
号用了费马小定理和是模的二次非剩余。然后
在算法实现的时候,对的选择可以随机,因为大约有一半数是模的二次非剩余,然后快速幂即可。
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(ll &x){
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
ll P;
inline ll Pow(ll a,ll b){
ll ret=1;
for (;b;b>>=1,a=a*a%P)
if (b&1)
ret=ret*a%P;
return ret;
}
inline ll legendre(ll a){
return Pow(a,(P-1)>>1);
}
struct abcd{
ll a,b,w; //a+b*sqrt(w)
abcd(ll a=0,ll b=0,ll w=0):a(a),b(b),w(w) { }
friend abcd operator *(abcd A,abcd B){
return abcd((A.a*B.a%P+A.b*B.b%P*A.w%P)%P,(A.a*B.b%P+A.b*B.a%P)%P,A.w);
}
};
inline abcd Pow(abcd a,int b){
abcd ret=abcd(1,0,a.w);
for (;b;b>>=1,a=a*a)
if (b&1)
ret=ret*a;
return ret;
}
inline ll Solve(ll n,ll p){
P=p;
if (P==2) return 1;
if (legendre(n)==P-1) return -1;
ll a,w;
while (1){
a=rand()%P;
w=((a*a-n)%P+P)%P;
if (legendre(w)==P-1) break;
}
return Pow(abcd(a,1,w),(P+1)>>1).a;
}
int main(){
srand(10086);
ll n,p,T,ans,a,b;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(T);
while (T--){
read(n); read(p);
ans=Solve(n,p);
if (ans==-1)
printf("No root\n");
else if (ans==p-ans)
printf("%lld\n",ans);
else if (ans<P-ans)
printf("%lld %lld\n",ans,P-ans);
else
printf("%lld %lld\n",P-ans,ans);
}
return 0;
}
以下来自:http://blog.csdn.net/acdreamers/article/details/10182281
接下来我们来解另一个二次同余方程的解,其中,并且是奇质数。方法如下
先求出方程的一个解,那么进一步有
我们知道
那么也就是说
可以证明和,那么最终得到
这里由于不是素数,所以求逆元用扩展欧几里得算法即可。