Official

G - Simultaneous Kagamimochi 2 Editorial by MMNMM


E 問題のこの解説の第二方針を使います。 (与えられた餅列の後に無限に大きな餅が無限に続いているとし、連続する \(K\) 個の餅をすべて上段にして \(K\) 個の鏡餅を作るために必要な下段のインデックスの最小値について考えます。)

\(B _ i\coloneqq i\) 番目の餅を上段の餅として使うときの、下段の餅としてありえる最小のインデックス \((1\leq i\leq N)\) と定めます。

すると、クエリ \((L,R)\) に対する答えは \(\displaystyle L+K-1+\max\left\lbrace\max _ {L\leq i\lt L+K}\lbrace B _ i-i\rbrace,K\right\rbrace\leq R\) を満たす最大の \(K\) です。 左辺は \(K\) について単調なので、\(B _ i-i\) をセグメント木などで管理し、二分探索を行うことでこの問題を解くことができます。

時間計算量は \(O(N+Q\log N)\) などになります。

実装例は以下のようになります。 セグメント木上の二分探索を行っており、時間計算量は \(O(N+Q\log N)\) 時間です。

#include <iostream>
#include <vector>
#include <atcoder/segtree>

int main() {
    using namespace std;
    unsigned N;
    cin >> N;

    vector<unsigned> A(N);
    for (auto&& a : A)
        cin >> a;

    // (区間の長さ, B[i] - i の最大値) を持つセグメント木
    using value_type = pair<unsigned, unsigned>;
    atcoder::segtree<value_type, [](const auto& lhs, const auto& rhs) {
        const auto& [left_length, left_distance_max]{lhs};
        const auto& [right_length, right_distance_max]{rhs};
        return value_type{left_length + right_length, max(left_distance_max, right_distance_max)};
    }, [] { return value_type{}; }> segment_tree{
        [N](const auto& seq) {
            vector<value_type> result(N);
            // B[i] を求める 全体で Θ(N) 時間
            for (unsigned i{}, j{}; i < N; ++i) {
                while (j < N && seq[i] * 2 > seq[j]) ++j;
                result[i] = {1, j - i};
            }
            return result;
        }(A)
    };

    unsigned Q;
    cin >> Q;

    for (unsigned i{}; i < Q; ++i) {
        unsigned L, R;
        cin >> L >> R;
        --L;
        // 右端が R 以下になる最大の [L, L+K) を求める
        cout << segment_tree.max_right(L, [L, R](const auto& itv) {
            const auto& [length, distance]{itv};
            return L + length + max(distance, length) <= R;
        }) - L << endl;
    }

    return 0;
}

posted:
last update: