ABC221 D - Online Games C++解答例
AtCoder Beginner Contest 221 D - Online GamesをC++で解きます。
問題
問題文をAtCoderのページより引用します。
問題文
あるオンラインゲームがあり、
人のプレイヤーが登録しています。 N
サービス開始日から日を迎えた今日、開発者である高橋くんがログイン履歴を調べたところ、 10^{100} 番目のプレイヤーはサービス開始日を1日目として、 i 日めから A_{i} 日間連続でログインし、それ以外の日はログインしていなかったことが判明しました。すなわち、 B_{i} 番目のプレイヤーはサービス開始日から、 i 日目に、かつそれらの日にのみログインしていたことが分かりました。 A_{i}, A_{i} + 1, \ldots, A_{i} + B_{i} - 1
をみたす各整数 1 \leq k \leq N について、サービス開始日から今日までの間で、ちょうど k 人がログインしていた日数を答えてください。 k
制約
1 \leq N \leq 2 \times 10^{5} 1 \leq A_{i} \leq 10^9 1 \leq B_{i} \leq 10^9 - 入力はすべて整数である。
解答例
imos法を用い…れない
与えられた区間に1を足すというクエリが
しかし制約を見ると、考慮しなければならない区間の最大幅は
イベントソートを使う
発想を変えてみます。あるプレイヤー
ここで、「ログインし始めたときのイベント」と「ログインしなくなったときのイベント」は区別するものとします。
イベントを
全ての
あるイベント
同様に、イベント
ここで、全ての
従って、「イベント
この問題ではログイン人数が0のときの日数は求められていないため、これらの期間は考慮しなくても良いことになります。
まとめると、現在のログイン人数をカウントしておくカウンタ変数
- イベント
がどちらのイベントかを確認するX_{j} - 「ログインし始めたイベント」のとき:カウンタ変数をインクリメントする
- 「ログインしなくなったイベント」のとき:カウンタ変数をデクリメントする
- 今、カウンタ変数
には「イベント\text{count} が起きたときの現在のログイン人数」が格納されているので、次にイベントが起こるまでの日数X_{j} 日を(X_{j + 1} - X_{j}) に足す\text{Ans}[\text{count}]
プログラムの実装例
実際にプログラムとして実装するときは、イベントを格納する配列をstd::vector<std::pair<int64_t, int64_t>>
型配列として定義しておきます。
ペアの内容は、{イベントが発生した日時、人数の変化}とします。人数の変化はそのイベントが「ログインし始めたイベント」なら1、「ログインしなくなったイベント」なら-1です。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
int64_t N;
std::cin >> N;
std::vector<std::pair<int64_t, int64_t>> X;
for (int64_t i = 0; i < N; i++) {
int64_t a, b;
std::cin >> a >> b;
X.push_back(std::make_pair(a, 1)); // ログインし始めたイベント
X.push_back(std::make_pair(a + b, -1)); // ログインしなくなったイベント
}
// 発生する日時が早い順にソート
std::sort(X.begin(), X.end());
// 答えを格納しておく配列
std::vector<int64_t> answer(N + 1, 0);
// 現在のログイン人数をカウントしておくカウンタ変数
int64_t count = 0;
// 発生が早い順にイベントを見ていく
for (int64_t i = 0; i < static_cast<int64_t>(X.size()) - 1; i++) {
// イベントXiが起きたときのログイン人数を更新
count += X.at(i).second;
// 次にイベントが起きるまでの日数を答えに加算
answer.at(count) += X.at(i + 1).first - X.at(i).first;
}
// 答えの出力
for (int64_t i = 1; i <= N; i++) {
if (i != 1) std::cout << " ";
std::cout << answer.at(i);
}
std::cout << std::endl;
return 0;
}
実際に提出したコードはこちら。
-
ある
についてイベントは2回発生するため、イベントの数はi 個となります ↩︎2N
Discussion