Official

D - Circle Lattice Points Editorial by QCFium


円を通る \(x = c\) (\(c\) は整数)と表せる直線は \(O(R)\) 個です。
それぞれについて、三平方の定理を使えば、その直線のうち円との共有部分の \(y\) 座標の範囲が高速に求まるので、\(x = c\) 上の円内の格子点の数が求まります。

しかし、ここで浮動小数点数の誤差が大きな問題になります。
例えば、\((X, Y) = (-0.0001, 0), R = 100000\) のときに \(x = 0\) の直線の円との共有部分の上限の計算過程で \((10^5)^2 - (10^{-4})^2\) を計算することになり、到底 double 型の精度を超えてしまいます。
この問題を解決するため、最初に \(X, Y, R\)\(10^4\) 倍して整数にします。そして、\(x, y\) が共に \(10^4\) の倍数である点を数えればよいです。
円を通る \(x = 10^4 c\) (\(c\) は整数)と表せる直線は先ほどと同様に \(O(R)\) しかないので、同様の解法で解くことができます。すべての計算が整数で行えるので誤差の心配はありません。

計算量は、平方根の計算に \(O(\log(R))\) かかるとして全体で \(O(R \log(R))\) です。
\(X, Y, R\) も入力時に正確な値では表せないことがほとんどであり、\(10^4\) 倍しても正確に整数ではない場合があります。そのため、 C++ であれば round 関数などで丸めてください。(C++では double を int に変換すると \(0\) 方向に切り捨てられるので、 \(0.5\) を足して int に変換するという方法で丸めようとすると負の場合に失敗します)

posted:
last update: