こういうのを得意になるぞー!!!
でも AtCoder でこういうデータ構造をしっかり準備する必要がある系は珍しい気がする。
問題概要
個のマス () があって、マス上に 個の区間がある。
各 に対して、 と移動したときに何個の区間を踏むかを答えよ (二度以上同じ区間を踏む場合は 1 個とカウントする)
制約
考えたこと
ここまでハッキリと「調和級数の和の計算量の構造になってるよ」と問題文に明記されているのは珍しい気がする。
個おきにぴょんぴょんするときに、同じ区間を二度踏んでしまう場合を扱うのが大変そう。でも少し考えると、
- 個おきに飛ぶとき、長さ 以上の区間は必ず踏む
ということがわかる。よって を小さい順に見ていくとき、区間を長さが短い順にソートしておくと、区間が左から順番に「絶対に踏むとは限らない」という側へと脱落して行くイメージ!!!!!
なので「脱落区間を重ね合わせたもの」というのを用意しておいて、毎回の ごとに、 から になった瞬間に長さ の区間があったならそれが脱落するので、それを「脱落区間を重ね合わせたもの」に足しあわせて行く。
そして「脱落区間」は個別にぴょんぴょんして数えてあげて、それに「脱落していない区間の個数」を足したものが答えになる。
残る問題は「脱落区間を重ね合わせたもの」に「脱落した区間」を足し合わせる部分であるが、これを でできるデータ構造がある。これを用いると一点の値の取得にも かかってしまうので、全体として
- 区間を長さが短い順にソート:
- 加算クエリ:
- ぴょんぴょんクエリ:
の計算時間となる。
https://github.com/drken1215/algorithm/blob/master/DataStructure/binary_indexed_tree_RAQ.cppgithub.com
#include <iostream> #include <vector> #include <algorithm> #include <numeric> using namespace std; template <class Abel> struct BIT { vector<Abel> dat[2]; Abel UNITY_SUM = 0; // to be set /* [1, n] */ BIT(int n) { init(n); } void init(int n) { for (int iter = 0; iter < 2; ++iter) dat[iter].assign(n + 1, UNITY_SUM); } /* a, b are 1-indexed, [a, b) */ inline void sub_add(int p, int a, Abel x) { for (int i = a; i < (int)dat[p].size(); i += i & -i) dat[p][i] = dat[p][i] + x; } inline void add(int a, int b, Abel x) { sub_add(0, a, x * -(a - 1)); sub_add(1, a, x); sub_add(0, b, x * (b - 1)); sub_add(1, b, x * (-1)); } /* a is 1-indexed, [a, b) */ inline Abel sub_sum(int p, int a) { Abel res = UNITY_SUM; for (int i = a; i > 0; i -= i & -i) res = res + dat[p][i]; return res; } inline Abel sum(int a, int b) { return sub_sum(0, b - 1) + sub_sum(1, b - 1) * (b - 1) - sub_sum(0, a - 1) - sub_sum(1, a - 1) * (a - 1); } /* debug */ void print() { for (int i = 1; i < (int)dat[0].size(); ++i) cout << sum(i, i + 1) << ","; cout << endl; } }; using pint = pair<int,int>; int N, M; vector<pint> inter; int main() { cin >> N >> M; inter.resize(N); for (int i = 0; i < N; ++i) cin >> inter[i].first >> inter[i].second, ++inter[i].second; sort(inter.begin(), inter.end(), [&](pint i, pint j) { return i.second - i.first < j.second - j.first;}); BIT<int> bit(M + 10); int pos = 0; for (int d = 1; d <= M; ++d) { while (pos < N && inter[pos].second - inter[pos].first <= d-1) { bit.add(inter[pos].first, inter[pos].second, 1); ++pos; } int res = 0; for (int i = d; i <= M; i += d) res += bit.sum(i, i+1); res += (N - pos); cout << res << endl; } }