二次剩余
PS:以下内容大概都是抄的。
大概是解这样一个方程:
这里讨论 (p) 为奇素数。
称非 (0) 数 (n) 是二次剩余当 (n) 存在上述方程的解,反之成为非二次剩余。
二次剩余的数量为 (frac{p-1}{2}),原因是这样的:
1.若一个数 (n) 为二次剩余,设它有两组解,(x_1,x_2)。
那么 (x_1^2 equiv x_2^2 equiv n),由平方差公式得到 ((x_1-x_2)(x_1+x_2) equiv 0)。
那么 (x_1 equiv -x_2),因为 (p) 为奇素数,所以 (x_1
eq x_2)。
2.若 (n) 为二次剩余,存在解 (x),(-x) 也为解。
欧拉准则
大概是判断一个数 (n) 是否是二次剩余。
做法是这样的,求 (n^{frac{p-1}{2}}),若为 (1) 则是,否则不是。
首先有这样一个事情,(n^{p-1} equiv 1),则 ((n^{frac{p-1}{2}})^2 equiv 1)。
若其取值为 (1),可以令 (n=g^k),其中 (g) 为原根。
则有 (g^{kfrac{p-1}{2}} equiv 1),即 ((p-1)| kfrac{p-1}{2})。
即 (2|k),所以 (n) 为二次剩余。
然后还有这样一个事情,若 (n) 为二次剩余,则存在 (x^2 equiv n)。
即 (n^{frac{p-1}{2}}equiv x^{p-1} equiv 1)。
所以 (n^{frac{p-1}{2}}equiv 1) 与 (n) 是二次剩余是等价的条件。
故可以通过 (n^{frac{p-1}{2}}) 为 (1) 或 (-1) 来判断 (n) 是否为二次剩余。
Cipolla
大概是对于 (n) 为二次剩余,找到 (x) 满足 (x^2 equiv n)。
做法是这样的,首先找到一个 (a),满足 (a^2-n) 为非二次剩余,这个过程通过随机并检验即可实现。
定义一个类似虚部的东西 (i),满足 (i^2 equiv a^2-n)。
然后有 ((a+i)^{p+1} equiv n),((a+i)^{frac{p+1}{2}}equiv x)。
有这样一些事情:
1.((A+B)^p=A^p + B^p)
通过二项式定理展开为 (sum limits_{j=0}^{p} A^j B^{p-j} inom{p}{j})。
其中 (inom{p}{j}) 中的质因子 (p) 只在 (j=0,j=p) 时不存在。
这样的话考虑 ((a+i)^{p+1}) 这个东西。
可以展开为 ((a+i)^p(a+i)=(a^p+i^p)(a+i)=(a-i)(a+i)=a^2-i^2=n)
故 ((a+i)^{frac{p+1}{2}}) 和 (-(a+i)^{frac{p+1}{2}}) 是两个解。
可以证明 ((a+i)^{frac{p+1}{2}}) 不存在虚部。
假设虚部存在,则有 ((x+yi)^2=n,y
eq 0)。
展开得 (x^2+y^2(a^2-n)-n=2xy)。
其中左侧不含虚部,故 (y
eq 0,x=0)。
代入得到 (y^2i^2=n),显然无解,与假设不成立。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int mod,n,a;
struct node{
ll r,i;
node(int r=0,int i=0):r(r),i(i){}
};
inline node operator * (const node &x,const node &y){
return node((x.r*y.r%mod+x.i*y.i%mod*(1ll*a*a%mod-n+mod))%mod,(x.r*y.i+x.i*y.r)%mod);
}
inline ll qpow(ll x,int k,ll r=1){
for(;k;k>>=1,x=x*x%mod) if(k&1) r=r*x%mod;
return r;
}
inline node qpow(node x,int k,node r=node(1,0)){
for(;k;k>>=1,x=x*x) if(k&1) r=r*x;
return r;
}
bool check(int x){
return qpow(x,mod-1>>1)==1;
}
int main(){
int T; scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&mod);
if(!n){ puts("0"); continue; }
if(!check(n)){ puts("Hola!"); continue; }
for(a=rand()%(mod-1)+1;check((1ll*a*a-n+mod)%mod);a=rand()%(mod-1)+1);
int x=qpow(node(a,1),mod+1>>1).r,y=mod-x; if(y<x) swap(x,y);
printf("%d %d
",x,y);
}
return 0;
}