---
# System prepended metadata

title: JOI2021 春合宿 Day1-3 フードコート (Food Court)
tags: [JOI]

---

---
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';
}
```