D - 183184 Editorial
by
sheyasutaka
考察
\(C + x\) の桁数が \(d\) であるときを考えます.このとき,\(x\) の範囲は以下の \(2\) 範囲の共通部分として得られます.
- \(1 \leq x \leq D\)
- \(10^{d-1} - C \leq x \leq 10^d - 1 - C\)
\(L = \mathrm{max}(1, 10^{d-1} - C),\ R = \mathrm{min}(D, 10^d - 1 - C)\) とおいたとき,\(L > R\) であればそのような \(x\) は存在せず,\(L \leq R\) であれば \(L \leq x \leq R\) が範囲です.
\(C + x\) の桁数が \(d\) のときを考えているので,\(f(C, C+x) = C \times 10^d + (C+x)\) となります.したがって,\(C \times 10^d + C + L\) 以上 \(C \times 10^d + C + R\) 以下の範囲にある平方数の個数がこの範囲の答えになります.
\(k\) 以下の正の平方数の個数は \(\lfloor \sqrt{k} \rfloor\) なので,求める答えは \(\lfloor \sqrt{C \times 10^d + C + R} \rfloor - \lfloor \sqrt{C \times 10^d + C + L - 1} \rfloor\) となります.
\(\lfloor \sqrt{k} \rfloor\) を求めるにあたっては,二分探索で求めるよりも std::sqrt 関数を利用するほうが高速です.
以上の計算を \(d = 1, \dots, \lfloor \log_{10} (C+D) \rfloor + 1\) に対して行い,答えを足し合わせることで,全体の答えが求まります.
平方根の計算が定数時間として,時間計算量は \(O(\log(C+D))\) となり,十分高速です.
実装例 (C++)
#include <iostream>
using std::cin;
using std::cout;
using std::min;
using std::max;
#include <cmath>
typedef long long int ll;
// floor(sqrt(x))
ll f (ll x) {
ll y = sqrt(x);
while (y * y > x) y--;
while ((y+1) * (y+1) <= x) y++;
return y;
}
ll solve (ll c, ll d) {
ll ans = 0;
ll xmin = 1, xmax = 9, cshift = 10;
while (xmin <= c + d) {
ll l = max(xmin, c + 1);
ll r = min(xmax, c + d);
if (l <= r) {
ll vl = c * cshift + l;
ll vr = c * cshift + r;
ans += (f(vr) - f(vl - 1));
}
xmin = xmin * 10;
xmax = (xmax + 1) * 10 - 1;
cshift *= 10;
}
return ans;
}
int main (void) {
ll T;
cin >> T;
for (ll ti = 0; ti < T; ti++) {
ll c, d;
cin >> c >> d;
ll ans = solve(c, d);
cout << ans << "\n";
}
return 0;
}
posted:
last update:
