大好き!!ガウス整数の問題リストがまた 1 個増えた!
問題概要
1 つの正の整数 が与えられる。
二次元平面上の任意の格子点を頂点としたグラフにおいて、距離の平方が であるような 2 点間に辺を張っていく。
こうしてできたグラフの連結成分の個数を求めよ。無限個ある場合には "inf" と出力せよ。
(というテストケースが 個与えられる)
制約
最初の考察
一般にガウス整数 が存在して が成り立つとき、 を任意のガウス整数として
で表されるような移動は可能となることがわかる。
たとえば のときは、連結成分は 1 になる。なぜならば、ガウス整数の世界で 5 を素因数分解すると、
となる。ここで、 と は互いに素であるから、あるガウス整数 が存在して、
が成立する。これはつまり、任意の格子点間の移動が可能であることを意味するのだ。
連結成分が 1 でない場合
では、連結成分が 1 でない場合はどんな場合だろうか。 の場合はダメだ。 と表す方法は本質的には
しかないが、 は単数倍で移り合う関係なので、互いに素ではない。つまり、mod の世界で等しいもの同士しか辺で結ばれないのだ。mod では、すべてのガウス整数は 2 種類に類別されるので、答えは 2 ということになる。
他にも、 という場合もある。この場合も と表す方法を考えたとき、 はからなず の倍数になる。つまり、mod の世界で等しいもの同士しか結ばれない。ガウス整数の世界では mod では、あまりが 49 個あるので、答えは 49 ということになる。
一般の場合
以上の考察を膨らませていくと、次の結論にいたる (ちゃんとした証明は tatyam さんの editorial より)。
を通常の整数の世界で素因数分解したとき
- で割って 余る素因数であって、指数が奇数であるものが存在するとき:"inf" (すべての格子点が孤立点になる)
- そうでないとき:素因数のうち、 および で割って 余る部分のみを残して計算した値が答え
たとえば、 のときは が答え。
計算量は となる。
コード
#include <bits/stdc++.h> using namespace std; vector<pair<long long, long long> > prime_factorize(long long n) { vector<pair<long long, long long> > res; for (long long p = 2; p * p <= n; ++p) { if (n % p != 0) continue; int num = 0; while (n % p == 0) { ++num; n /= p; } res.push_back(make_pair(p, num)); } if (n != 1) res.push_back(make_pair(n, 1)); return res; } void solve() { long long R; cin >> R; const auto &pf = prime_factorize(R); bool ok = true; long long res = 1; for (auto [p, e] : pf) { if (p % 4 == 3 && e % 2 == 1) ok = false; if (p % 4 == 1) continue; for (long long i = 0; i < e; ++i) res *= p; } if (ok) cout << res << endl; else cout << "inf" << endl; } int main() { int T; cin >> T; while (T--) solve(); }