二次剩余
猛然发现我这玩意还没补。赶紧补上。
二次剩余就是解这么个东西:
我们通常用 Cipolla 算法解决二次剩余问题(Cipolla 只适用于奇素数)。
是否有解
假设 \(n\) 不是 \(0\)。
我们有欧拉判别法:当且仅当 \(n^{\frac {p-1}2}\equiv 1\pmod p\) 时有解。小证一波。
首先我们知道 \(n^{p-1}\equiv 1\pmod p\),所以上边那个式子只能是 \(\pm 1\)。
然后假如说 \(n^{\frac {p-1}2}\equiv -1\pmod p\),又有 \(x^2\equiv n\pmod p\),则 \(x^{p-1}=n^{\frac {p-1}2}\equiv -1\pmod p\) 显然不对。
那么剩下的就是二次剩余了。
Cipolla 算法
首先我们找个 \(a\) 满足 \(a^2-n\) 不是二次剩余。由于二次剩余占一半,随机化就行了。
然后设 \(i\) 满足 \(i^2=a^2-n\)。这里的 \(i\) 是个类似于虚数单位的东西,也就是在模 \(p\) 剩余系里不存在。所以为了运算得写个复数类。
这时候一定有 \((a+i)^{p+1}\equiv n\pmod p\)。再证一波。
引理:\(i^p\equiv-i\)
证明:\(i^p=i(a^2-n)^{\frac{p-1}2}\equiv-i\)。
引理:\((a+b)^p=a^p+b^p\)。
证明:二项式定理展开得: \((a+b)^p=\sum_{i=0}^p\binom pia^ib^{p-i}\)。
对组合数使用 Lucas 定理: \(\dbinom pi=\dbinom 1{i/p}\dbinom 0{i\bmod p}\)。显然当且仅当 \(i=0\) 或 \(p\) 时有值。
那么:
此时 \((a+i)^{\frac {p+1}2}\) 就是我们要的一个解,相反数是另一个解。
代码:
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
int n,I,mod;
struct cp{
int r,i;
cp(int a=0,int b=0){r=a;i=b;}
bool operator==(const cp &s)const{
return r==s.r&&i==s.i;
}
cp operator+(const cp &s)const{
return cp{(r+s.r)%mod,(i+s.i)%mod};
}
cp operator-(const cp &s)const{
return cp{(r-s.r+mod)%mod,(i-s.i+mod)%mod};
}
cp operator*(const cp &s)const{
int real=(1ll*r*s.r%mod+1ll*I*i%mod*s.i%mod)%mod;
int imag=(1ll*r*s.i%mod+1ll*i*s.r%mod)%mod;
return cp{real,imag};
}
};
cp qpow(cp a,int b){
cp ans=1;
while(b){
if(b&1)ans=ans*a;
a=a*a;
b>>=1;
}
return ans;
}
bool check(int x){
return qpow(cp(x),(mod-1)>>1)==1;
}
pair<int,int> getsqrt(int n){
int a=rand()%mod;
while(!a||check((1ll*a*a-n+mod)%mod))a=rand()%mod;
I=(1ll*a*a-n+mod)%mod;
int x=qpow(cp(a,1),(mod+1)>>1).r;
return make_pair(x,mod-x);
}
signed main(){
int tim;scanf("%d",&tim);
while(tim--){
scanf("%d%d",&n,&mod);
if(!check(n)){
if(n==0)puts("0");
else puts("Hola!");
continue;
}
pair<int,int>ans=getsqrt(n);
if(ans.first>ans.second)swap(ans.first,ans.second);
if(ans.first==ans.second)printf("%d\n",ans.first);
else printf("%d %d\n",ans.first,ans.second);
}
return 0;
}