【AtCoder】ABC 480 E - Product-Bounded Arithmetic Sequences

実行時間制限: 2 sec / メモリ制限: 1024 MiB / Difficulty: 1355 / NoviSteps: 1D / 配点: 450 点
問題概要
以下の条件を全て満たす正整数列 を良い数列と定める。
- は等差数列である。
良い数列のうち、要素の総積が 以下であるものの個数を求めよ。 なお、制約の条件下で答えが 以下となることが証明できる。
制約
- は整数
考察
一見 が大きすぎてどうしようもなさそうだが、実は、数列の長さ が長くなるほど、条件を満たす初項 と公差 の組み合わせは急激に減少していく。
例えば、 の場合、
を満たす組 の個数を数えればよい。
このとき、 より が必要で、 の範囲の整数 が探索対象になる。
さらに、 を固定すれば総積は に関して単調増加するため、二分探索で条件を満たす最大の を求めることができる。
これを一般化すると、長さ の数列に対して、初項 の探索範囲は であり、 を固定すれば二分探索で条件を満たす最大の公差 を求めることができる。
また、初項・公差がともに 1 の場合が積を最小にするので、
を満たす だけ調べれば十分。
なので、 まで調べればよい。
最後に計算量について。
長さ の数列について、初項は高々 通り、二分探索は 回、それぞれの判定に 時間がかかる。
したがって、全体としては、
となり、本問の制約下では十分高速である。
実装例
1.#include <bits/stdc++.h>2.using namespace std;3. 4.#define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++)5.#define repe(i, start, end) for (auto i = (start); (i) <= (end); (i)++)6. 7.constexpr long long INFL = 1e+18;8. 9.using ll = long long;10. 11.// ======================================== //12. 13.int main()14.{15. ll N;16. cin >> N;17. 18. auto check = [&](ll a, ll d, ll l) -> bool {19. ll prod = 1;20. rep(i, 0, l) {21. ll next = a + i * d;22. if (next > N / prod) return false;23. 24. prod *= next;25. }26. 27. return true;28. };29. 30. ll ans = 0;31. repe(l, 3, 20) {32. for (ll a = 1; check(a, 1, l); a++) {33. ll ok = 1, ng = INFL;34. while (ng - ok > 1) {35. ll mid = (ok + ng) / 2;36. if (check(a, mid, l)) ok = mid;37. else ng = mid;38. }39. 40. ans += ok;41. }42. }43. 44. cout << ans << endl;45. 46. return 0;47.}実装時間: 40分
コメント
が小さい場合で実験してみると、意外と途方もない問題ではないことが分かって拍子抜け。





