E - 図書館の蔵書点検 / Library Inventory Check Editorial by admin
gpt-5.5-xhigh概要
各貸し出し計画について、区間 \([L_j, R_j]\) の中で \(D_i \leq T_j\) を満たす書棚の利益 \(A_i \times V_i\) の合計を求める問題です。
「日付 \(T\) までに修復済みの書棚だけを有効にする」と考え、クエリを日付順に処理します。
考察
計画 \(j\) の答えは次の式です。
\[ \sum_{\substack{L_j \leq i \leq R_j \\ D_i \leq T_j}} A_i \times V_i \]
ここで、書棚 \(i\) の利益を
\[ W_i = A_i \times V_i \]
とおくと、問題は「\(D_i \leq T_j\) を満たす位置 \(i\) の重み \(W_i\) について、区間和を求める」問題になります。
素朴に各クエリごとに \(L_j\) から \(R_j\) まで全探索すると、最悪で \(O(NQ)\) かかります。
\(N + Q \leq 2 \times 10^5\) なので、これは間に合いません。
重要な気づきは、日付 \(T\) が大きくなるほど、条件 \(D_i \leq T\) を満たす書棚は増えるだけであることです。
例えば、クエリを \(T\) の昇順に処理するとします。
- ある時点で \(T = 5\) のクエリを処理する
- その次に \(T = 8\) のクエリを処理する
このとき、新たに追加されるのは \(6 \leq D_i \leq 8\) の書棚だけです。
すでに \(D_i \leq 5\) の書棚は追加済みなので、再計算する必要がありません。
したがって、
- 書棚を \(D_i\) の昇順に並べる
- クエリを \(T_j\) の昇順に並べる
- 現在の日付までに修復済みになった書棚を Fenwick Tree に追加する
- クエリの区間和を Fenwick Tree で求める
という方法で高速に解けます。
アルゴリズム
まず、各書棚について以下の情報を持ちます。
- 修復完了日 \(D_i\)
- 位置 \(i\)
- 利益 \(W_i = A_i \times V_i\)
これらを書棚リストとして、\(D_i\) の昇順にソートします。
また、各クエリについて以下の情報を持ちます。
- 左端 \(L_j\)
- 右端 \(R_j\)
- 日付 \(T_j\)
- 元のクエリ番号 \(j\)
クエリも \(T_j\) の昇順にソートします。
ただし、出力は入力順に行う必要があるため、元のクエリ番号を保存しておきます。
Fenwick Tree には、現在のクエリの日付 \(T\) までに修復済みの書棚の利益だけを入れます。
処理の流れは以下の通りです。
- 書棚を \(D_i\) の昇順にソートする
- クエリを \(T_j\) の昇順にソートする
- Fenwick Tree を空で用意する
- 書棚リストの先頭を指すポインタ \(p\) を用意する
- クエリを日付の昇順に処理する
- まだ追加していない書棚のうち、\(D_i \leq T_j\) を満たすものをすべて Fenwick Tree に追加する
- Fenwick Tree で区間 \([L_j, R_j]\) の和を求める
- 答えを元のクエリ番号の位置に保存する
- 最後に答えを入力順に出力する
Fenwick Tree では、位置 \(i\) に値 \(W_i\) を追加し、区間和
\[ \text{sum}(R) - \text{sum}(L - 1) \]
によって \([L, R]\) の合計を求めます。
計算量
- 時間計算量: \(O(N \log N + Q \log Q + (N + Q) \log N)\)
- 空間計算量: \(O(N + Q)\)
実装のポイント
答えは最大で \(2 \times 10^{13}\) なので、
intではなくlong longを使います。\(A_i \times V_i\) も
long longで計算します。Fenwick Tree は 1-indexed で扱うと、書棚番号 \(1, 2, \ldots, N\) と対応して実装しやすいです。
クエリをソートすると入力順が崩れるため、元のクエリ番号
idを保存しておきます。条件は \(D_i \leq T_j\) です。第 \(D_i\) 日目を含むので、比較は
<ではなく<=にします。ソースコード
#include <bits/stdc++.h>
using namespace std;
struct Fenwick {
int n;
vector<long long> bit;
Fenwick(int n) : n(n), bit(n + 1, 0) {}
void add(int idx, long long val) {
for (; idx <= n; idx += idx & -idx) bit[idx] += val;
}
long long sum(int idx) const {
long long res = 0;
for (; idx > 0; idx -= idx & -idx) res += bit[idx];
return res;
}
long long range_sum(int l, int r) const {
return sum(r) - sum(l - 1);
}
};
struct Item {
int d;
int pos;
long long w;
};
struct Query {
int l, r, t, id;
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, Q;
cin >> N >> Q;
vector<Item> items(N);
for (int i = 1; i <= N; i++) {
long long A, D, V;
cin >> A >> D >> V;
items[i - 1] = {(int)D, i, A * V};
}
vector<Query> queries(Q);
for (int j = 0; j < Q; j++) {
int L, R, T;
cin >> L >> R >> T;
queries[j] = {L, R, T, j};
}
sort(items.begin(), items.end(), [](const Item& a, const Item& b) {
return a.d < b.d;
});
sort(queries.begin(), queries.end(), [](const Query& a, const Query& b) {
return a.t < b.t;
});
Fenwick fw(N);
vector<long long> ans(Q);
int p = 0;
for (const auto& q : queries) {
while (p < N && items[p].d <= q.t) {
fw.add(items[p].pos, items[p].w);
p++;
}
ans[q.id] = fw.range_sum(q.l, q.r);
}
for (int i = 0; i < Q; i++) {
cout << ans[i] << '\n';
}
return 0;
}
この解説は gpt-5.5-xhigh によって生成されました。
posted:
last update: