BEN2のブログ

たまに書いています。

ABC 170 E - Smart Infants

ABC 170 E - Smart Infants についてのメモを残します。

multiset

set は要素の重複を許さないが、multiset は重複を許すデータ構造となっている。

#include <bits/stdc++.h>
using namespace std;

int main() {
  multiset<int> S = { 1, 2, 2, 3, 3, 3 };
  cout << S.size() << endl;  // 6
  S.erase(3);
  cout << S.size() << endl;  // 3
  S.erase(S.find(2));
  cout << S.size() << endl;  // 2
}

erase 関数に引数として要素を渡すと、その要素が全て削除されている。イテレータを渡すと、イテレータが指す要素 1 個のみが削除されている。

PAST?

下図は、入力例 1 における初期状態。

f:id:BEN2suzuka:20200615214601p:plain

  • 各幼稚園の最大レートを高速に取得する
  • 最強園児たちの最低レート (平等さ) を高速に取得する

を実現したい。これらは set を用いればよさそう。
2 種類 set を用意する。1 つは、各幼稚園のレートの集合 (200,000 個の set)。もう 1 つは、最強園児たちのレートの集合。
ただし、1 つの幼稚園に同レートの幼児が複数人 所属する状況や、異なる幼稚園の最強園児のレートが同一となる状況があり得るので、両方とも要素の重複を許す multiset で実装する。

さて、クエリ処理は「幼児  x を幼稚園  y へ転園させる」といった内容。

  • 幼児  x がどの幼稚園に所属しているか
  • 幼児  x のレートはいくらか

という情報を保持しておく必要がある。
下図は、入力例 1 における「幼児 4 を幼稚園 3 へ転園させる」の様子を表したもの。転園前、幼児 4 は幼稚園 1 に所属しており、レートは 1 である。

f:id:BEN2suzuka:20200615214625p:plain

この図で「変わるかも」と指摘している箇所があるが、この転園では値は変わっていない。
実装でやることは以下の通り。

  • 幼稚園 1 と幼稚園 3 の最大のレート  R_{1}, \, R_{3} を取得し、最強園児たちのレートの集合から  R_{1}, \, R_{3} を削除。ここでは  R_{1} = 8, \, R_{3} = 9
  • 幼稚園 1 のレートの集合から 1 を削除し、幼稚園 3 のレートの集合に 1 を追加
  • 幼稚園 1 と幼稚園 3 の最大のレート  R_{1}^{\prime}, \, R_{3}^{\prime} を取得し、最強園児たちのレートの集合に  R_{1}^{\prime}, \, R_{3}^{\prime} を追加。ここでは  R_{1}^{\prime} = 8, \, R_{3}^{\prime} = 9
  • 幼児 4 が所属する幼稚園の番号を 3 に更新
  • 最強園児たちのレートの集合の最小値 (平等さ) を出力

誰も所属していない幼稚園の存在や、multiset の erase 関数の挙動に気をつけながら実装していく。

#include <bits/stdc++.h>
using namespace std;

int main() {
  int N, Q; cin >> N >> Q;
  vector<int> A(N), B(N);
  vector<multiset<int>> R(200000);  // 幼稚園 i のレートの集合
  multiset<int> M;  // 最強園児たちのレートの集合
  for (int i = 0; i < N; i++) {
    cin >> A.at(i) >> B.at(i);
    B.at(i)--;
    R.at(B.at(i)).insert(A.at(i));
  }
  for (int i = 0; i < 200000; i++) {
    if (!R.at(i).empty()) M.insert(*R.at(i).rbegin());
  }

  for (int i = 0; i < Q; i++) {
    int c, d; cin >> c >> d;
    c--; d--;
    int prev = B.at(c);  // 転園前

    // M から幼稚園 prev と幼稚園 d の最大レートを削除
    M.erase(M.find(*R.at(prev).rbegin()));
    if (!R.at(d).empty()) M.erase(M.find(*R.at(d).rbegin()));

    // 幼稚園 prev のレートの集合から A[c] を削除
    R.at(prev).erase(R.at(prev).find(A.at(c)));

    // 幼稚園 d のレートの集合に A[c] を追加
    R.at(d).insert(A.at(c));

    // M に幼稚園 prev と幼稚園 d の最大レートを追加
    if (!R.at(prev).empty()) M.insert(*R.at(prev).rbegin());
    M.insert(*R.at(d).rbegin());

    B.at(c) = d;  // 所属情報の更新

    cout << *M.begin() << endl;
  }
}