数论。费马平方和定理。
费马平方和 : 奇质数能表示为两个平方数之和的充分必要条件是该质数被 4 除余 1 。
翻译过来就是:当且仅当一个自然数的质因数分解中,满足 4k+3 形式的质数次方数均为偶数时,该自然数才能被表示为两个平方数之和。
因此我们对 c 进行 Pollard-Rho 质因数分解,再判断满足 4k+3 形式的质因子的次方数是否均为偶数即可。期间可使用32位快速 Miller-Rabin 算法并使用SplitMix32自定义哈希加快umap存储优化。
时间复杂度:$O(\log (c))$ —— 全Lc站最低复杂度,执行0ms
空间复杂度:$O(\log (c))$
class Solution {
public:
struct Hash {
static uint32_t splitmix32(uint32_t x) {
x += 0x9e3779b;
x = (x ^ (x >> 15)) * 0xbf58476;
x = (x ^ (x >> 13)) * 0x94d049b;
return x ^ (x >> 14);
}
size_t operator()(uint32_t x) const {
static const uint32_t FIXED_RANDOM = rand();
return splitmix32(x + FIXED_RANDOM);
}
};
int fastgcd(int a, int b) {
if (!a || !b)
return a | b;
unsigned shift = __builtin_ctz(a | b);
a >>= __builtin_ctz(a);
do {
b >>= __builtin_ctz(b);
if (a > b)
swap(a, b);
b -= a;
} while (b);
return a << shift;
}
int qpow(int a, int b, int m) {
int ret = 1;
while (b) {
if (b & 1)
ret = (long long)ret * a % m;
b >>= 1;
a = (long long)a * a % m;
}
return ret;
}
bool MillerRabin(int n) {
if (n < 3 or n % 2 == 0)
return n == 2;
if (n % 3 == 0)
return n == 3;
int u = n - 1, t = 0;
while (u % 2 == 0)
u /= 2, ++t;
int dict[] = {2, 7, 61};
for (int a : dict) {
if (a % n <= 1)
return 1;
int v = qpow(a, u, n);
if (v == 1)
continue;
int s;
for (s = 0; s < t; ++s) {
if (v == n - 1)
break;
v = (long long)v * v % n;
}
if (s == t)
return 0;
}
return 1;
}
int PollardRho(int x) {
int s = 0, t = 0, c = rand() % (x - 1) + 1;
int step = 0, goal = 1, val = 1;
for (goal = 1;; goal <<= 1, s = t, val = 1) {
for (step = 1; step <= goal; ++step) {
t = ((long long)t * t + c) % x;
val = (long long)val * abs(t - s) % x;
if (step % 127 == 0) {
int d = fastgcd(val, x);
if (d > 1)
return d;
}
}
int d = fastgcd(val, x);
if (d > 1)
return d;
}
}
vector<int> getfac(int x) {
vector<int> fac;
unordered_map<int, int, Hash> primes;
function<void(int)> get = [&](int x) {
if (x < 2) {
return;
}
if (MillerRabin(x)) {
primes[x]++;
return;
}
int mx = PollardRho(x);
get(x / mx);
get(mx);
};
get(x);
vector<pair<int, int>> tmp(primes.begin(), primes.end());
int sz = tmp.size();
function<void(int, int)> dfs = [&](int id, int x) {
if (id == sz) {
fac.push_back(x);
return;
}
int p = 1;
for (int i = 0; i <= tmp[id].second; i++) {
if (i != 0) {
p *= tmp[id].first;
}
dfs(id + 1, x * p);
}
};
dfs(0, 1);
sort(fac.begin(), fac.end());
return fac;
}
bool judgeSquareSum(int c) {
vector<int> fac = getfac(c);
for (int i : fac) {
if (i == 1 or c % i)
continue;
int exp = 0;
while (c % i == 0) {
c /= i;
exp++;
}
if (i % 4 == 3 and exp & 1)
return false;
}
return c % 4 != 3;
}
};