二次剩余学习笔记
二次剩余 | Quadratic Residue
求解方程 。
如果该方程有正整数解,称 是 的二次剩余。以下设 是及素数。
解的数量
有正整数解的 有 个。这里的 。
勒让德符号 | Legendre Symbol
欧拉准则
欧拉准则的证明
根据费马定理知 。 那么显然 。因为把 移过去,因式分解可知。
假设存在 。那么 ,根据定义 。
证毕。
Cipolla 算法
找到一个 使得 。
定义 ,则 即为答案。
证明
。
注意到 ,原因是用二项式定理展开,除了 和 两项,其余都存在因子 。
然后呢根据费马定理的推论,,并且我们求出了 ,所以 。
证毕。
实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll i2, n, p;
struct Complex{
ll r, i;
Complex(ll r = 0, ll i = 0): r(r), i(i) {}
};
Complex operator * (Complex x, Complex y){
Complex ans = Complex(0, 0);
ans.r = (x.r * y.r % p + x.i * y.i % p * i2 % p + p) % p, ans.i = (x.r * y.i % p + x.i * y.r % p + p) % p;
return ans;
}
ll qpow(Complex a, ll b, ll p){
Complex ans = Complex(1, 0);
while(b){if(b & 1) ans = ans * a; a = a * a, b >>= 1; }
return ans.r % p;
}
ll qpow(ll a, ll b, ll p){
a %= p; ll ans = 1;
while(b){if(b & 1) ans = ans * a % p; a = a * a % p, b >>= 1; }
return ans;
}
inline ll rnd(){
return 1ll * rand() * rand() % p;
}
ll Cipolla(ll n, ll p){
assert(p % 2);
if(n != 0 && qpow(n, (p - 1) >> 1, p) == (p - 1)) return -1;
ll x = 0; do x = rnd(); while(qpow(i2 = (x * x - n + p) % p, (p - 1) >> 1, p) != (p - 1));
return (qpow((Complex){x, 1}, (p + 1) >> 1, p) + p) % p;
}
int T;
int main(){
srand(54255 * 355 + 292);
cin >> T;
while(T--){
cin >> n >> p; if(n == 0){cout << 0; continue;}
ll ans = Cipolla(n, p), ans2 = p - ans;
if(ans == -1) {cout << -1 << endl; continue; }
else{
cout << 1 << ' ';
if(ans > ans2) cout << ans2 << ' ' << ans << endl;
else cout << ans << ' ' << ans2 << endl;
}
}
}