---
tags: JOI
---
# JOI2021 春合宿 Day1-3 フードコート (Food Court)
## 問題文
https://www.ioi-jp.org/camp/2021/2021-sp-tasks/day1/foodcourt.pdf
https://atcoder.jp/contests/joisc2021/tasks/joisc2021_c
## 考察
クエリを順に走査して加入や脱退を管理しようとすると、何人目がどの団体かを管理するのが大変そうです。(何人並んでいるかを別に求めた後永続セグ木 + 二分探索で log 2 つかな)
そこで、店舗を順に走査しましょう。加入イベント・脱退イベントの挿入・削除をして、ある時刻での先頭から $B$ 番目がどの団体かを求められればいいです。
こういう retroactive なやつの実現方法といえば、セグ木です。どの団体か求めるのはなかなか難しいですが、何人並んでいるかくらいはわかるでしょう。
(脱退イベントの人数, 加入イベントの人数) は $(a_1, b_1) + (a_2, b_2) = (a_1 + \max(a_2 - b_1, 0), b_2 + \max(b_1 - a_2, 0))$ でマージでき、単位元は $(0, 0)$ なので、これをセグ木に乗せれば何人並んでいるかがわかります。
これで後ろから何番目かがわかるので、あとは加入イベントのみのセグ木で二分探索を行えば、どのタイミングでの加入かがわかります。
## 実装
$O(N + Q \log Q)$
https://atcoder.jp/contests/joisc2021/submissions/29604941
```cpp=
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct Event{
// 以前に並んでいた人のうち leave 人までが去って、新たに join 人が加わる
ll leave = 0, join = 0;
Event operator+(const Event& a) const {
if(join <= a.leave) return {leave + a.leave - join, a.join};
else return {leave, join + a.join - a.leave};
}
};
template<class T> struct SegmentTree{
vector<T> data;
ll size = 1;
SegmentTree(ll n){
while(n > size) size <<= 1;
data.resize(size * 2);
}
void set(ll i, T a){
i += size;
data[i] = a;
while(i >>= 1) data[i] = data[i << 1] + data[i << 1 | 1];
}
// fold [0, r)
T get(ll r) const {
T ans{};
ll l = size;
r += size;
while(l != r){
if(r & 1) ans = data[--r] + ans;
l >>= 1;
r >>= 1;
}
return ans;
}
// min l s.t. f(get(l, r)) == true
template<class F> ll min_left(ll r, F f) const {
ll l = size;
r += size;
T a{};
while(1){
if(l == r) return 0;
if(r & 1){
T b = data[r - 1] + a;
if(f(b)){
a = b;
r--;
}
else break;
}
l >>= 1;
r >>= 1;
}
while(l < size){
l <<= 1;
r <<= 1;
T b = data[r - 1] + a;
if(f(b)){
a = b;
r--;
}
}
return r - size;
}
};
int main(){
cin.tie(nullptr);
ios::sync_with_stdio(false);
ll N, M, Q;
cin >> N >> M >> Q;
// {フードコート, クエリ時刻, leave, join}
vector<array<ll, 4>> events;
// {フードコート, クエリ時刻, 前から B 人目, 何番目の出力}
vector<array<ll, 4>> query;
vector<ll> ans;
// クエリ時刻 -> 団体番号
vector<ll> group(Q);
for(ll t = 0; t < Q; t++){
ll type;
cin >> type;
if(type == 1){
ll L, R, C, K;
cin >> L >> R >> C >> K;
L--;
group[t] = C;
events.push_back({L, t, 0, K});
events.push_back({R, t, 0, 0});
}
else if(type == 2){
ll L, R, K;
cin >> L >> R >> K;
L--;
events.push_back({L, t, K, 0});
events.push_back({R, t, 0, 0});
}
else{
ll A, B;
cin >> A >> B;
A--;
query.push_back({A, t, B, (ll)ans.size()});
ans.emplace_back();
}
}
sort(events.begin(), events.end(), greater<>{});
sort(query.begin(), query.end(), greater<>{});
SegmentTree<Event> seg(Q);
// join の総和を数える。前から B 人目を見つけるのに使う。
SegmentTree<ll> seg2(Q);
for(ll f = 0; f < N; f++){
while(events.size() && events.back()[0] == f){
auto [f, t, leave, join] = events.back();
events.pop_back();
seg.set(t, {leave, join});
seg2.set(t, join);
}
while(query.size() && query.back()[0] == f){
auto [f, t, B, i] = query.back();
query.pop_back();
// 後ろから B 人目 (0-indexed)
B = seg.get(t).join - B;
if(B < 0) continue;
// 時刻 t から遡って B 人を超える瞬間が答えの団体
const ll t2 = seg2.min_left(t, [&, B = B](ll b){ return b <= B; }) - 1;
ans[i] = group[t2];
}
}
for(ll x : ans) cout << x << '\n';
}
```