D - スタンプラリー / Stamp Rally Editorial by admin Editorial for printing
gpt-6-astra-medium概要
連続する区間のうち、全 \(7\) 種類のスタンプを含むものの個数を求めます。区間の右端を固定し、各種類が最後に現れた位置から、条件を満たす左端の個数を数えます。
考察
すべての区間を調べると間に合わない
区間 \((l,r)\) の候補は全部で \(\frac{N(N+1)}{2}\) 個あります。左端ごとに右端を伸ばしながらスタンプの種類を管理しても、時間計算量は \(O(N^2)\) です。
\(N\) は最大 \(10^6\) なので、区間を一つずつ調べる方法では間に合いません。
右端を固定し、各種類の「最後の出現位置」を考える
右端 \(r\) を固定します。チェックポイント \(1\) から \(r\) までの中で、種類 \(s\) が最後に現れた位置を \(\mathrm{last}[s]\) とします。まだ現れていない種類については、\(\mathrm{last}[s]=0\) とします。
区間 \([l,r]\) に種類 \(s\) が含まれる条件は、次のとおりです。
\(l \le \mathrm{last}[s]\)
実際、最後の出現位置が \(l\) 以上なら、その位置は区間内にあります。逆に、その位置が \(l\) 未満なら、種類 \(s\) は区間内にはありません。
したがって、全 \(7\) 種類が含まれるための必要十分条件は、
\(l \le \min(\mathrm{last}[1],\mathrm{last}[2],\dots,\mathrm{last}[7])\)
です。この最小値を \(m\) とすると、条件を満たす左端は \(1,2,\dots,m\) の ちょうど \(m\) 個 になります。
まだ現れていない種類がある場合は \(m=0\) となり、条件を満たす区間がないことも自然に表せます。
具体例
スタンプの列が次のようになっているとします。
\(1,2,3,4,5,6,7,1\)
右端が \(r=7\) のとき、各種類の最後の出現位置は、
\((1,2,3,4,5,6,7)\)
です。最小値は \(1\) なので、有効な区間は \([1,7]\) の \(1\) 個です。
右端が \(r=8\) になると、種類 \(1\) の最後の出現位置だけが更新され、
\((8,2,3,4,5,6,7)\)
となります。最小値は \(2\) なので、有効な区間は \([1,8]\) と \([2,8]\) の \(2\) 個です。
このように、右端ごとの個数を足し合わせれば、すべての有効な区間を重複なく数えられます。
アルゴリズム
- 各種類の最後の出現位置を管理する配列
lastを、すべて \(0\) で初期化します。 - 答えを格納する変数
answerを \(0\) にします。 - 右端 \(r\) を \(1\) から \(N\) まで順に動かし、次の処理を行います。
- スタンプの種類 \(C_r\) を読み込みます。
- 種類 \(C_r\) の最後の出現位置を \(r\) に更新します。
lastの最小値をanswerに加算します。
answerを出力します。
各右端について、条件を満たす左端の個数を正確に加算しています。また、各区間の右端は一意なので、数え漏れも重複もありません。
計算量
- 時間計算量: \(O(7N)=O(N)\)
各チェックポイントで、長さ \(7\) の配列の最小値を求めます。 - 空間計算量: \(O(7)=O(1)\)
各種類の最後の出現位置だけを保持し、入力列全体を保存する必要はありません。
実装のポイント
チェックポイントの位置は \(1\) 始まりにします。
これにより、初期値の \(0\) を「まだ現れていない」という意味で使えます。array<int, 7> last{};は全要素を \(0\) で初期化します。スタンプの種類と配列の添字を対応させます。
種類は \(1\) から \(7\)、配列の添字は \(0\) から \(6\) なので、種類cの更新はlast[c - 1] = r;とします。最後の出現位置を更新してから最小値を加算します。
右端 \(r\) のスタンプも含めた状態で、区間の個数を数えるためです。答えには 64 bit 整数型を使います。
区間の総数は最大で \(\frac{N(N+1)}{2}\) 個あり、答えが 32 bit 整数型に収まらない場合があります。提示コードではlong longを使用しています。ソースコード
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N;
cin >> N;
array<int, 7> last{};
long long answer = 0;
for (int r = 1; r <= N; ++r) {
int c;
cin >> c;
last[c - 1] = r;
answer += *min_element(last.begin(), last.end());
}
cout << answer << '\n';
return 0;
}
この解説は gpt-6-astra-medium によって生成されました。
posted:
last update: