二次剩余

猛然发现我这玩意还没补。赶紧补上。

二次剩余就是解这么个东西:

\[x^2\equiv n\pmod p \]

我们通常用 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)^{p+1}=(a+i)^p(a+i)=(a^p-i^p)(a+i)\equiv(a-i)(a+i)=a^2-i^2=n \]

此时 \((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;
}
posted @ 2023-01-29 21:21  gtm1514  阅读(40)  评论(0编辑  收藏  举报