楽しかった
問題概要
正の整数 が与えられる。
以下の条件を満たす正の整数 の個数を求めよ:
を
で割ったときの商と余りが一致する
制約
考えたこと
とりあえず について手元で計算しているうちに、商と余りとが一致した値
として考えられるものは
くらいの大きさにならないことが見えてきた。具体的には
となったときに、余りの定義から なので
という感じになる。よって
くらいまで試せば OK。
計算量は 。
#include <iostream> #include <vector> using namespace std; long long solve(long long N) { long long res = 0; for (long long r = 1; r*r <= N; ++r) { if ((N-r) % r == 0 && (N-r)/r > r) res += (N-r)/r; } return res; } int main() { long long N; cin >> N; cout << solve(N) << endl; }
直接的に求める
さらに直接求めることもできて、
と表せるので、
は
の約数である
であってこれは
と同値である
ということがわかる。したがって
の約数
を列挙して
- そのうち
を満たすものについて
を合計する
としてもよい。ここで、 が int 型にもおさまらないような値だと
を計算すると 64 bit 整数にもおさまらないことが気になるが、
とかだと
になることから
とはなりえないことを利用して弾くことができる。
計算量は約数計算がボトルネックで、やは 。
#include <iostream> #include <vector> using namespace std; vector<long long> calc_divisor(long long n) { vector<long long> res; for (long long i = 1LL; i*i <= n; ++i) { if (n % i == 0) { res.push_back(i); long long j = n / i; if (j != i) res.push_back(j); } } sort(res.begin(), res.end()); return res; } long long solve(long long N) { auto div = calc_divisor(N); long long res = 0; for (auto r : div) { if (r > 1000000) continue; if (N > r*r + r) res += (N-r)/r; } return res; } int main() { long long N; cin >> N; cout << solve(N) << endl; }