G - sqrt(n²+n+X) 解説 by MMNMM


\(n\) が条件を満たすなら \(-n-1\) も条件を満たすことから、\(n\ge0\) の条件を課した解を列挙すればよいです。

整数 \(k\) について \(\sqrt{n ^ 2+n+X}=n+k\) が成り立っているとして式を整理します。 \(n+k\ge0\) かつ \(n ^ 2+n+X=n ^ 2+2kn+k ^ 2\) となって、第 \(2\) 式を \(n\) について解くと \(n=\dfrac{X-k ^ 2}{2k-1}\) となります。

\(n\ge0,n+k\ge0\) より \(n(n+k)(2k-1) ^ 2\ge0\) となるので、\((X-k ^ 2)(X+k(k-1))\ge0\) を得ます。

  • \(X\gt0\) のとき、\(X+k(k-1)\gt0\) なので \(X-k ^ 2\ge0\) でなければならず、\(-\sqrt X\le k\le\sqrt X\) が必要です。
  • \(X\lt0\) のとき、\(X-k ^ 2\lt0\) なので \(X+k(k-1)\le0\) でなければならず、\(\dfrac{1-\sqrt{1-4X}}2\le k\le\dfrac{1+\sqrt{1-4X}}2\) が必要です。
  • \(X=0\) のとき、\(-k ^ 3(k-1)\ge0\) より、\(k=0,1\) が必要です。

以上より、\(-\sqrt{|X|}\le k\le1+\sqrt{|X|}\) の範囲を探索すれば十分であることがわかります。

よって、この範囲の \(k\) に対して \(\dfrac{X-k ^ 2}{2k-1}\) が条件を満たすか判定すればこの問題を解くことができます。

実装例は以下のようになります。

#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>

int main(){
    using namespace std;
    long X;
    cin >> X;
    
    // 探索する k の範囲
    // abs(X) は double で正確に表現できるので、この平方根の小数点以下を切り捨てた値も正確に得られる
    long bound = sqrt(abs(X)) + 1;
    
    vector<long> ans;
    for (long k{-bound + 1}; k <= bound; ++k)
        if ((X - k * k) % (2 * k - 1) == 0) { // 割り切れることが必要
            // n ≧ 0 かつ n + k ≧ 0 なら OK
            auto n = (X - k * k) / (2 * k - 1);
            if (n >= 0 && n + k >= 0) {
                ans.emplace_back(n);
                ans.emplace_back(-n - 1);
            }
        }
        
    ranges::sort(ans);
    
    cout << size(ans) << endl;
    for (const auto n : ans)
        cout << n << endl;

    return 0;
}

投稿日時:
最終更新: