H - Good bye, AtCoder Land. 解説
by
potato167
\(B\) の小さい方から \(K\) 個の総和が \(T\) より大きいときは、答えが \(K\) 未満になります。このとき、乗るべきアトラクションは \(B\) の小さい順で良いです。
答えが \(K\) 以上であるときを考えます。 乗るアトラクションを決め打ったとき、ファストパスを使うべきアトラクションは \(A_{i} - B_{i}\) の大きい順に \(K\) 個です。
アトラクションが \(A_{i} - B_{i}\) の大きい順に並んでいると仮定します。\(K\) 個以上乗るアトラクションを決めると、index が \(a\) 以上なら普通に乗り、 \(a\) 未満ならファストパスで乗るような、整数 \(a\) が存在します。このような \(a\) を決め打ちます。
すると、\(a\) 未満の乗り物から \(K\) 個乗ることになりますが、これは \(B\) の小さい順に乗れば良いです。 \(B_{i} (i < a)\) の小さい方から \(K\) 個の総和を \(S_{a}\) とします。\(S_{a}\) は priority_queue を用いて \(O(N \log(N))\) で求まります。
そうすると結局、\(a = K + 1, K + 2, \dots, N\) に対して以下の問題が解ければ良いです。
\(A_{a}, A_{a + 1}, \dots, A_{N}\) から総和が \(T - S_{a}\) より小さくなるように要素をいくつか選ぶとき、その要素の数の最大値を求めてください。
これは例えば \(A\) が昇順になるような index の列を求めておけば、 segment tree の max_right を用いて \(O(N\log(N))\) 解くことができます。
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
#define rep(i,a,b) for (int i=(int)(a);i<(int)(b);i++)
#define all(p) p.begin(),p.end()
#include <atcoder/segtree>
struct F{
ll a = 0;
ll c = 0;
};
F op(F l, F r){
l.a += r.a;
l.c += r.c;
return l;
}
F e(){
return F{};
}
ll target;
bool f(F x){
return x.a <= target;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
ll N, K, T;
cin >> N >> K >> T;
vector<pair<ll, ll>> A(N);
vector<ll> B(N);
rep(i, 0, N) cin >> A[i].first >> A[i].second, B[i] = A[i].second;
// A[i] の昇順に並べる
sort(all(A));
sort(all(B));
B.resize(K);
ll bsum = 0;
for (auto x : B) bsum += x;
// 答えが T 未満になるとき
if (bsum > T){
ll sum = 0;
int ind = 0;
while (sum + B[ind] <= T) sum += B[ind++];
cout << ind << "\n";
return;
}
vector<int> order(N);
rep(i, 0, N) order[i] = i;
// order は B[i] - A[i] の降順
sort(all(order), [&](int l, int r) {
return A[l].first - A[l].second > A[r].first - A[r].second;
});
ll ans = K;
ll sum = 0;
priority_queue<ll> pq;
atcoder::segtree<F, op, e> seg(N);
rep(i, 0, N) seg.set(i, {A[i].first, 1});
auto op = [&](int ind) -> void {
ind = order[ind];
seg.set(ind, e());
sum += A[ind].second;
pq.push(A[ind].second);
if ((int)pq.size() > K){
sum -= pq.top();
pq.pop();
}
};
rep(i, 0, K) op(i);
rep(i, K, N){
target = T - sum;
if (target > 0){
int b = seg.max_right<f>(0);
ans = max(ans, K + seg.prod(0, b).c);
}
op(i);
}
cout << ans << "\n";
}
投稿日時:
最終更新: