F - Rated Range Editorial by shinchan

fenwick-tree+いもす法

だいたい公式解説と同じです。

fenwick-treeといもす法を使うと、以下のようなクエリが \(O(\log N)\) でできます。

  • \(a[l], a[l+1], ... , a[r - 1]\)\(x\) を加える

  • \(a[x]\) を出力

fenwick-treeでは、一点add、区間sumが出来ますが、上の1つ目のクエリを「 \(a[l]\)\(x\) を加えて \(a[r]\)\(-x\) を加える」、2つ目のクエリを「\(a[0],a[1], ..., a[x]\) の総和を出力」とすることで上記クエリを実現できます。

fenwick-tree上でも二分探索が愚直だと \(O((\log N)^2)\) のところを \(O(\log N)\) でできます (が、C++などの高速な言語だと愚直でも通りますし、実装が悪いのか、そんなに実行時間変わりませんでした)。

以下の計算量は \(X = 500000\) としています。

実装例 ( \(O((X+N+Q)\log X)\) , C++, 493 ms)

https://atcoder.jp/contests/abc389/submissions/61841120

#include <bits/stdc++.h>
using namespace std;
int N = 500010;

template <class T> struct fenwick_tree {
    int n;
    vector<T> a;
    fenwick_tree(int m) : n(m), a(m + 1, 0) {}
    void add(int x, T y) {
        x ++;
        while(x <= n) {
            a[x] += y;
            x += x & -x;
        }
    }
    T sum(int x) {
        T r = 0;
        while(x > 0) {
            r += a[x];
            x -= x & -x;
        }
        return r;
    }
    T sum(int x, int y) { // [x, y), 0-indexed
        return sum(y) - sum(x);
    }
    int lower_bound(T x) {
        int id = 0;
        for(int i = __lg(n); i >= 0; i --) {
            if((id | (1 << i)) > n) continue;
            if(a[id | (1 << i)] < x) {
                x -= a[id | (1 << i)];
                id |= (1 << i);
            }
        }
        return id;
    }
};

int main() {
    int n;
    cin >> n;
    fenwick_tree<int> fw(N);
    for(int i = 1; i < N; i ++) fw.add(i, 1);
    for(int i = 0; i < n; i ++) {
        int a, b;
        cin >> a >> b;
        int id1 = fw.lower_bound(a);
        if(fw.sum(0, N) > b + 1) {
            int id2 = fw.lower_bound(b + 1);
            fw.add(id1, 1);
            fw.add(id2, -1);
        } else {
            fw.add(id1, 1);
        }
    }
    int q; cin >> q;
    for(int i = 0; i < q; i ++) {
        int x; cin >> x;
        cout << fw.sum(0, x + 1) << endl;
    }
    return 0;
}

実装例 (27行目~37行目の部分のみ変更して記載, \(O((X + Q)\log X + N (\log X)^2)\) , C++, 555 ms)

https://atcoder.jp/contests/abc389/submissions/61841100

    int lower_bound(T x) {
        int le = 0, ri = N;
        while(ri - le > 1) {
            int mid = (ri + le) / 2;
            if(sum(0, mid) >= x) {
                ri = mid;
            } else {
                le = mid;
            }
        }
        return le;
    }

posted:
last update: