橋梁管理日誌

日誌と言いながら日次での更新はされない模様

ABC128 E Roadwork

問題ページ

解法

人々は整数時刻に座標0を出発し、速度1で正の方向に歩き続けます。よって、時刻S-0.5から時刻T-0.5まで座標Xを通行止めにするということは、時刻[S-X, T-X)に出発した人は座標Xで通行止めに当たるということになります。人々が歩いている途中に通行止めに当たった場合はそこで歩くのをやめるので、出発する時刻を被覆する区間のうち最も小さいXまで歩き続けるということになります(そのような区間がなければ無限に歩き続けます)。これは遅延評価セグメント木を使えば解くことができます。時刻はそのままでは使えないので座標圧縮をしますが、このとき工事関連の時刻だけではなく、人々が出発する時刻もまとめて座圧すると実装が楽になります。セグ木の更新順は、各クエリでは最小のXを求めたいのでXの大きい順に更新します。以下の実装では区間取得を行うセグ木を用いていますが、この問題では一点取得に対応していれば十分です。

実装

提出コード - https://atcoder.jp/contests/abc128/submissions/6933093
work:=第一成分がX、第二成分の第一成分がS-X、第二成分の第二成分がT-X

#include<iostream>
#include<vector>
#include<limits>
#include<unordered_map>
#include<algorithm>
#include<utility>
using namespace std;

using work = pair<int, pair<int, int> >;

constexpr int INF = 1 << 30;

// 区間更新区間min
template<class T>
class SegmentTree{
    int n;
    vector<T> node, upd;
    vector<bool> flag;

    void evaluate(int k, int l, int r){
        if(flag[k]){
            node[k] = upd[k];
            flag[k] = false;
            if(r - l > 1){
                upd[k * 2 + 1] = upd[k];
                flag[k * 2 + 1] = true;
                upd[k * 2 + 2] = upd[k];
                flag[k * 2 + 2] = true;
            }
        }
        return;
    }

    void update(int a, int b, int k, int l, int r, T x){
        evaluate(k, l, r);
        if(b <= l || r <= a){
            return;
        }
        if(a <= l && r <= b){
            upd[k] = x;
            flag[k] = true;
            evaluate(k, l, r);
            return;
        }

        update(a, b, k * 2 + 1, l, (l + r) / 2, x);
        update(a, b, k * 2 + 2, (l + r) / 2, r, x);

        node[k] = min(node[k * 2 + 1], node[k * 2 + 2]);

        return;
    }

    T get(int a, int b, int k, int l, int r){
        evaluate(k, l, r);
        if(b <= l || r <= a){
            return numeric_limits<T>::max();
        }
        if(a <= l && r <= b){
            return node[k];
        }

        T vl = get(a, b, k * 2 + 1, l, (l + r) / 2);
        T vr = get(a, b, k * 2 + 2, (l + r) / 2, r);

        return min(vl, vr);
    }

public:
    // 要素数または配列でコンストラクト
    SegmentTree(int siz = 0){
        init(siz);
    }

    SegmentTree(const vector<T> v){
        build(v);
    }

    // 指定された要素数で初期化
    void init(int siz){
        n = 1;
        while(n < siz){
            n <<= 1;
        }

        node.resize(n * 2 - 1);
        fill(node.begin(), node.end(), numeric_limits<T>::max());

        upd.resize(n * 2 - 1);

        flag.resize(n * 2 - 1);
        fill(flag.begin(), flag.end(), false);

        return;
    }

    // 指定された配列で初期化
    void build(const vector<T> v){
        init((int)v.size());

        for(int i = 0, i_len = (int)v.size() ; i < i_len ; ++i){
            node[i + n - 1] = v[i];
        }

        for(int i = n - 2 ; i >= 0 ; --i){
            node[i] = min(node[i * 2 + 1], node[i * 2 + 2]);
        }

        return;
    }

    // [a, b)をxに更新
    void update(int a, int b, T x){
        update(a, b, 0, 0, n, x);
        return;
    }

    // [a, b)の取得
    T get(int a, int b){
        return get(a, b, 0, 0, n);
    }
};

template<typename T>
int compress(vector<T> v, unordered_map<T, int> &zip){
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
    for(int i = 0, i_len = (int)v.size() ; i < i_len ; ++i){
        zip[v[i]] = i;
    }
    return (int)v.size();
}

int main(){
    cin.tie(0);
    ios::sync_with_stdio(false);

    int n, q;
    cin >> n >> q;

    vector<int> ti;

    vector<work> v;
    for(int i = 0 ; i < n ; ++i){
        int s, t, x;
        cin >> s >> t >> x;
        v.push_back({x, {s - x, t - x}});
        ti.push_back(s - x);
        ti.push_back(t - x);
    }

    vector<int> query;
    for(int i = 0 ; i < q ; ++i){
        int d;
        cin >> d;
        query.push_back(d);
        ti.push_back(d);
    }

    unordered_map<int, int> zip;
    int m = compress(ti, zip);

    SegmentTree<int> seg(m);

    sort(v.begin(), v.end(), [](work l, work r){ return l.first > r.first; });
    for(int i = 0 ; i < n ; ++i){
        seg.update(zip[v[i].second.first], zip[v[i].second.second], v[i].first);
    }

    for(auto &d : query){
        int ans = seg.get(zip[d], zip[d] + 1);
        if(ans < INF){
            cout << ans << endl;
        }else{
            cout << -1 << endl;
        }
    }

    return 0;
}