【AtCoder】ABC 444 E - Sparse Range

実行時間制限: 2 sec / メモリ制限: 1024 MiB / Difficulty: 1107 / NoviSteps: ??? / 配点: 450 点
問題概要
長さ の整数列 と正整数 が与えられるので、以下の条件をともに満たす整数の組 の個数を求めよ。
- を満たす全ての整数の組 について、 である
制約
- 入力される値は全て整数
考察
ある区間 が条件を満たすとき、その部分区間( など)も条件を満たす。
したがって、尺取り法によって、 を固定したときの条件を満たす最大の を求めることができる。
ここで、 が条件を満たしているとして、右端を に進めるときに、条件を満たすかどうかを判定する必要がある。
愚直にやるなら、新たな要素 を と全て比較して、 であるかを確認すればよいが、これは の計算量がかかってしまう。
しかしよく考えると、 がソートされていれば、追加する に隣接する値のみを確認すればよいことが分かる。
理由 (クリックで展開)
昇順ソートされた数列 について、新たな数 を追加するとする。
このとき、 ならば、当然 も成り立つ。
右側についても同様で、 ならば も成り立つ。
したがって、 の要素をソートされた状態で管理し、 をキーとして二分探索を行い、以下の2つの条件を満たすかを確認すればよい。
- 以上の最小値 について である。
- 未満の最大値 について である。
これはsetとそれに備わっているlower_boundメソッドを用いれば簡単に実装でき、計算量は となる。
実装例
1.#include <bits/stdc++.h>2.using namespace std;3. 4.#define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++)5. 6.using ll = long long;7. 8.// ======================================== //9. 10.int main()11.{12. int N;13. ll D;14. cin >> N >> D;15. vector<ll> A(N);16. rep(i, 0, N) cin >> A[i];17. 18. ll ans = 0;19. set<ll> st;20. int R = 0;21. rep(L, 0, N)22. {23. while (R < N)24. {25. auto itr = st.lower_bound(A[R]);26. 27. if (itr != st.end() && abs(*itr - A[R]) < D)28. break;29. if (itr != st.begin() && abs(*prev(itr) - A[R]) < D)30. break;31. 32. st.insert(A[R]);33. R++;34. }35. 36. ans += R - L;37. 38. st.erase(A[L]);39. }40. 41. cout << ans << endl;42. 43. return 0;44.}実装時間: 20分
コメント
尺取り法のいい練習問題だと思う。





