Please sign in first.
Official
E - 図書館の蔵書検索 / Library Book Search Editorial
by
E - 図書館の蔵書検索 / Library Book Search Editorial
by
kyopro_friends
この問題はクエリ先読み+セグメントツリーにより解くことができます。
まずは次の問題を考えます。
問題:\(N\) 個の棚があり、最初全て空である。以下の2種類のクエリを順に処理せよ
- \(i\) が与えられる。棚 \(i\) に本を 1 冊置く
- \(L,R\) が与えられる。番号が \(L\) 以上 \(R\) 以下である棚に置かれている本の数を求める
この問題はセグメントツリーや Fenwick Tree を用いて解くことができます。
元の問題を考えます。 \(Q\) 回の検索を先読みすることで、検索は \(T\) の降順に行われるとしてよいです。このとき、問題は次のように読み替えることができます。
問題:\(N\) 個の棚があり、最初全て空である。また、棚に置かれていない本が \(M\) 冊ある。以下のクエリを順に処理せよ
- まだ棚に置かれていない本のうち、ページ数が \(T\) 以上の本を指定された棚に置く。その後、棚の番号が \(L\) 以上 \(R\) 以下である棚に置かれている本の数を求める
クエリが \(T\) の降順に与えられることから、本を棚から取り除く必要がないことに注意してください。
この問題は予め本をページ数の降順にソートしておくことで、先に述べた問題と同じになるため解くことができます。
計算量は \(O(Q\log Q+Q\log N+M\log M)\) となります。
実装例 (C++)
#include <bits/stdc++.h>
#include <atcoder/fenwicktree>
using namespace std;
int main() {
int n, m, q, k;
cin >> n >> m >> q >> k;
vector<pair<int,int>>ds(m);
for(int i=0; i<m; i++){
int s, d;
cin >> s >> d;
s--;
ds[i] = {d, s};
}
sort(ds.rbegin(), ds.rend());
vector<tuple<int,int,int,int>>query(q);
for(int i=0; i<q; i++){
int l, r, t;
cin >> l >> r >> t;
l--;
query[i] = {t, l, r, i};
}
sort(query.rbegin(), query.rend());
vector<int>ans(q);
int pos = 0;
atcoder::fenwick_tree<int>seg(n);
for(auto [t, l, r, i]: query){
while(pos < m && ds[pos].first >= t){
seg.add(ds[pos].second, 1);
pos++;
}
ans[i] = max(seg.sum(l, r) - k, 0);
}
for(int i=0; i<q; i++){
cout << ans[i] << endl;
}
}
実装例 (Python)
from atcoder.fenwicktree import FenwickTree
N, M, Q, K = map(int, input().split())
DS = []
for _ in range(M):
S, D = map(int, input().split())
S -= 1
DS.append((D, S))
DS.sort(reverse=True)
query = []
for i in range(Q):
L, R, T = map(int, input().split())
L -= 1
query.append((T, L, R, i))
query.sort(reverse=True)
ans = [0] * Q
pos = 0
seg = FenwickTree(N)
for t, l, r, i in query:
while pos < M and DS[pos][0] >= t:
seg.add(DS[pos][1], 1)
pos += 1
ans[i] = max(seg.sum(l, r) - K, 0)
print(*ans, sep="\n")
posted:
last update:
