競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 128E問題

\(\def \set #1#2{\{ #1 \ \vert \ #2 \}}\)

ABC128E

簡易化: 変数の固定

まずは人と座標を固定して考える. 時刻と座標の二つの単位があるので,一方に統一して判定式を作る. ここでは,時刻に注目した式で判定する. 人 \(i \in Q\) が座標 \(x\) を訪れた時点で 工事中であるのは, \(d_{i}+x \in [s,t)\) と同値. すなわち, \(d_{i} \in [s-x,t-x)\) と同値.

解法 0: Event sort

時刻毎にイベントを作る. 優先順位に注意する. 同じ時刻なら, (工事が終わる) > (工事が始まる) > (人が訪れる) の優先順位になる. それに応じて,タイプに番号を付ける.

  • Type -1: Erase: 座標 \(x\) の工事が終わる
  • Type 1: Add: 座標 \(x\) の工事が始まる
  • Type 2: Visit: 人 \(i\) が座標 \(x\) を訪れる

あとはイベントを時刻でソートして実行する. 時刻 \(time\) のイベントを実行しているとき, \(time\) 未満のイベントは全て終わっている.
Type のイベントが起きたとき, 人 \(i\) に対する答えを求めたい. 時刻でイベントをソートしているから, \(i\) が訪れる以前の工事の処理(開始と終了)は全て実行済みである. よって,あとは工事されている座標全体のうち, 最小の値を取得すればよい. これは,工事中の座標を set で持っておけば良い.

解法 1: 答えの全探索

問われているのは (人 \(i) \mapsto \) (座標 \(x) = ans_{i}\) であるが,逆に \(x \mapsto i\) を求める. つまり,

答えを全探索して,対応する相手を求める.

座標 \(x\) を全探索したいので, \(\ (x, \cdots)\ \) の形でソートした配列を考える. \(x\) においてもっておきたい情報は \(s,t\) である. \(s-x, t-x\) は \(s,t\) があれば作れるので持っておく必要が無い.
\(x\) を全探索したら,対応する人 \(i\) を全て求めるが, それは \(d_{i} \in [s-x,t-x)\) と同値. \(\ (x,s,t)\ \) を知っている状態で,この様な \(d_{i}\) を求めるには \(d_{i}\) の set を持っておいて,binary search すれば良い.
注意: \(x\) が答えと決めた \(d_{i}\) は, 最初の工事で足を止めるため \(x\) より後ろが答えになる事はない. よって,\(d_{i}\) を set から削除しないと正しい答えが求まらない. このとき,iterator の更新に注意.削除したせいで iterator がずれるので, it = ds.erase(x); の様に書くのが正しい.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

// ---- event sort
int main() {
  ll n, q;
  cin >> n >> q;

  vector<tuple<ll,ll,ll>> ev;
  rep(i,n){
    ll s,t,x; cin >> s >> t >> x;
    // 同じ時刻は削除優先
    ev.emplace_back(s-x, +1, x);  
    ev.emplace_back(t-x, -1, x);  
  }
  rep(i,q){
    ll d; cin >> d;
    // 同じ時刻は工事優先
    ev.emplace_back(d, 2, i);
  }

  sort(all(ev));
  set<ll> xs;
  vll ans(q,-1);
  for(auto [time, type, x]: ev){ // time は使わない
    if(type == +1){
      xs.insert(x);
    }else if(type == -1){
      xs.erase(x);
    }else{
      // x : index of people
      if(xs.size()) ans[x] = *xs.begin();
    }
  }

  rep(i,q){
    cout << ans[i] << endl;
  }

  return 0;
}

// ------ 答えの全探索
int main() {
  ll n, q;
  cin >> n >> q;

  vector<tuple<ll,ll,ll>> v;
  rep(i,n){
    ll s,t,x; cin >> s >> t >> x;
    v.emplace_back(x, s, t);
  }
  sort(all(v));

  set<pll> ds;
  rep(i,q){
    ll d; cin >> d;
    ds.insert({d,i});
  }

  // ans: people i -> position x
  // full search of x
  vll ans(q,-1);
  for(auto [x,s,t]: v){
    auto it = ds.lower_bound({s-x, -1});
    // it = [d,i]
    while(it != ds.end() && it->first < t-x){
      ans[it->second] = x;
      // rem: 答えが確定したので erase.
      // erase することで,これ以降の座標で重複して調べて上書きされずに済む.
      it = ds.erase(it);
    }
  }

  coutv(ans);

  return 0;
}