Official

D - Happy New Year 2023 Editorial by yuto1115

解説

与えられた整数 NN を素因数分解するアルゴリズムとしては、i=1,2,,Ni=1,2,\dots,\lfloor\sqrt{N}\rfloor の順に NNii で割り切れるかどうかを確かめる O(N)O(\sqrt{N}) のアルゴリズムが有名です (試し割りと呼ばれることが多いです)。

なぜ N\lfloor\sqrt{N}\rfloor まで試し割りするだけで良いのか NNN\lfloor\sqrt{N}\rfloor 以下の素因数で割り切れるだけ割って、残った数を NN' とします。N=1N'=1 の場合は既に全ての素因数を見つけたということなので、N>1N' > 1 の場合を考えます。ここで、NN'N+1\lfloor\sqrt{N}\rfloor+1 以上の素因数しか持ち得ないこと、N+1\lfloor\sqrt{N}\rfloor+1 以上の数を 22 つ以上掛け合わせると NN を超えてしまう (明らかに NN{N'} \leq N なので、NN'22 つ以上の素因数を持つと仮定すると矛盾が生じます) ことから、NN' は素数であることがわかります。よって、最初の試し割りで見つけた素因数と合わせることで素因数分解が完了します。

しかし、本問題では NN が大きいため、O(N)O(\sqrt{N}) の試し割りは間に合いません。そこで、N=p2qN=p^2q (p,qp,q は素数) と表せるという条件を使います。

重要な性質として、 min(p,q)N3\min(p,q)\leq\sqrt[3]{N} が成り立ちます (これが成り立たないと仮定すると、p2q>Np^2q>N となって矛盾します)。よって、N\sqrt{N} まで試し割りしなくとも、N3\sqrt[3]{N} まで試し割りすれば ppqq のうち少なくとも一方は見つかることがわかります。この方針の下、以下のように具体的なアルゴリズムを構築可能です。

  1. i=2,3,,N3i=2,3,\dots,\lfloor\sqrt[3]{N}\rfloor のうち、NNii で割り切れるような最小の iiAA とおく (A=pA=p または A=qA=q である)。
  2. NA\frac{N}{A}AA で割り切れるか調べる。割り切れるなら、p=A,q=NA2p=A,q = \frac{N}{A^2} である。割り切れないなら、p=NA,q=Ap = \sqrt{\frac{N}{A}},q=A である。

よって、11 テストケースあたり O(N3)O(\sqrt[3]{N}) でこの問題を解くことができました。

なお、Pollard’s rho algorithm などの高速な素因数分解アルゴリズムを用いることでもこの問題を解くことができます。

実装例 (C++) :

Copy
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. using ll = long long;
  4. int main() {
  5. int t;
  6. cin >> t;
  7. while (t--) {
  8. ll n;
  9. cin >> n;
  10. ll p = 0, q = 0;
  11. for (ll i = 2; i * i * i <= n; i++) {
  12. if (n % i != 0) continue;
  13. if ((n / i) % i == 0) {
  14. p = i;
  15. q = n / i / i;
  16. } else {
  17. q = i;
  18. p = (ll) round(sqrt(n / i));
  19. }
  20. break;
  21. }
  22. cout << p << ' ' << q << endl;
  23. }
  24. }
#include<bits/stdc++.h>

using namespace std;

using ll = long long;

int main() {
    int t;
    cin >> t;
    while (t--) {
        ll n;
        cin >> n;
        
        ll p = 0, q = 0;
        for (ll i = 2; i * i * i <= n; i++) {
            if (n % i != 0) continue;
            if ((n / i) % i == 0) {
                p = i;
                q = n / i / i;
            } else {
                q = i;
                p = (ll) round(sqrt(n / i));
            }
            break;
        }
        cout << p << ' ' << q << endl;
    }
}

posted:
last update:



2025-04-09 (Wed)
00:45:45 +00:00