二次剩餘學習筆記
阿新 • • 發佈:2020-07-22
學習: https://kewth.blog.luogu.org/solution-p5491
定義
對於p,n,若存在x,滿足\(x^2 \equiv n \pmod{p}\),則稱n為模p意義下的二次剩餘,即n在模p意義下能開方,計算二次剩餘就是計算x,x在模p意義下和\(\sqrt{n}\)等價
下文僅對p為奇素數時進行討論
判定
如何判斷n是否是模p意義下的二次剩餘?
p為奇素數,有如下結論:
(1)a是模p的二次剩餘的充要條件是\(a^{(p-1)/2} \equiv 1 \pmod{p}\)
(2)a是模p的非二次剩餘的充要條件是\(a^{(p-1)/2} \equiv -1 \pmod{p}\)
(3)當a時模p的二次剩餘時,同餘方程有兩個解
性質
二次剩餘有如下特性:
(1) 若a1,a2都是模p的二次剩餘,a1*a2也是模p的二次剩餘
(2) 若a1,a2都是模p的非二次剩餘,a1*a2是模p的二次剩餘
(3) 若a1是模p的二次剩餘,a2是模p的非二次剩餘,a1*a2是模p的非二次剩餘
(4)$ n^2 \equiv (p−n)^2 \pmod{p}$
(5) p的二次剩餘和二次非剩餘的個數均為(p−1)/2
求解方法
首先判斷這個數數n是否是二次剩餘,如果n是0則答案只有一個,就是0
若n不為0,且有二次剩餘,則進行如下運算:
隨機一個數a,使得\(w=a^2-n\)為非二次剩餘,
然後\((a+i)^{(p+1)/2}\)
程式碼
/*洛谷P5491 若有解,則按模p後遞增的順序輸出在模p意義下的全部解. 若兩解相同,只輸出其中一個, 若無解,則輸出Hola! */ #include <cstdio> #include <cstdlib> typedef long long ll; int mod; ll I_mul_I; // 虛數單位的平方 struct complex { ll real, imag; complex(ll real = 0, ll imag = 0): real(real), imag(imag) { } }; inline bool operator == (complex x, complex y) { return x.real == y.real and x.imag == y.imag; } inline complex operator * (complex x, complex y) { return complex((x.real * y.real + I_mul_I * x.imag % mod * y.imag) % mod, (x.imag * y.real + x.real * y.imag) % mod); } complex power(complex x, int k) { complex res = 1; while(k) { if(k & 1) res = res * x; x = x * x; k >>= 1; } return res; } bool check_if_residue(int x) {//判斷x是否是模p意義下的二次剩餘 return power(x, (mod - 1) >> 1) == 1; } void solve(int n, int p, int &x0, int &x1) { mod = p; ll a = rand() % mod; while(!a or check_if_residue((a * a + mod - n) % mod)) a = rand() % mod; I_mul_I = (a * a + mod - n) % mod; x0 = int(power(complex(a, 1), (mod + 1) >> 1).real); x1 = mod - x0; } int main (){ int T; scanf("%d",&T); while(T--){ int n,p,x0,x1; scanf("%d%d",&n,&p); mod = p; if(n==0){//若n為0,有一個解0 printf("0\n"); } else if(check_if_residue(n)){//n不為0且有解,則必有一對不同解 solve(n,p,x0,x1); if(x0<x1){ printf("%d %d\n",x0,x1); } else{ printf("%d %d\n",x1,x0); } } else{ printf("Hola!\n"); } } }