公式
B - ロボットの宝集め / Robot's Treasure Collection 解説
by
B - ロボットの宝集め / Robot's Treasure Collection 解説
by
MMNMM
印刷ページ
制約から、一回の操作 2 を \(O(Q)\) 時間で(十分高速に)シミュレーションできれば、全体で \(O(SQ)\) 時間となり実行時間制限に間に合いそうです。
ロボットの移動をシミュレーションするのはそのまま実装しても \(O(Q)\) 時間になります。 「現在いる部屋に宝があるか(すでに回収されていないか)」を判定する部分は少し工夫をする必要があります。 例えば以下のような工夫をすることで、シミュレーション全体を \(O(Q)\) 時間や \(O(Q\log Q)\) 時間とすることができます。
- 現在のシミュレーションで宝を回収した部屋の一覧を平衡二分探索木やハッシュテーブルを使って管理する
- それぞれの部屋に宝があるかと、現在のシミュレーションで宝を回収した部屋の一覧を配列で管理し、シミュレーションが完了したら今回のシミュレーションで回収した部屋だけに宝を戻す
- それぞれの部屋について、最後に宝を回収したのが何回目の操作によるシミュレーションかを管理する
実装例は以下のようになります。
#include <iostream>
#include <vector>
#include <set>
using namespace std;
int main() {
int N, Q, M;
cin >> N >> Q >> M;
vector<int> A(N);
for (int& a : A) {
cin >> a;
}
string S;
cin >> S;
for (int i = 0; i < M; ++i) {
int type;
cin >> type;
if (type == 1) { // 書き換え操作は
int p;
char c;
cin >> p >> c;
S[p - 1] = c; // そのまま書き換える
} else { // スコアはシミュレーションで求める
set<int> collected_index; // 今回のシミュレーションで回収した宝の一覧
int now_index = 0; // 現在の部屋
long ans = 0; // 現在の獲得スコア
for (char c : S) {
if (c == 'L') {
now_index = max(0, now_index - 1);
}
if (c == 'R') {
now_index = min(N - 1, now_index + 1);
}
if (c == 'P') {
// まだその部屋から回収していなければ
if (!collected_index.contains(now_index)) {
ans += A[now_index]; // スコアを獲得
}
collected_index.emplace(now_index);
}
if (c == 'B') {
now_index = 0;
}
}
cout << ans << endl;
}
}
return 0;
}
N, Q, M = map(int, input().split())
A = list(map(int, input().split()))
S = list(input())
for i in range(M):
type, *arg = input().split()
if int(type) == 1: # 書き換え操作は
p, c = arg
S[int(p) - 1] = c # そのまま書き換える
else: # スコアはシミュレーションで求める
collected_index = set() # 今回のシミュレーションで回収した宝の一覧
now_index = 0 # 現在の部屋
ans = 0 # 現在の獲得スコア
for c in S:
if c == 'L':
now_index = max(0, now_index - 1)
if c == 'R':
now_index = min(N - 1, now_index + 1)
if c == 'P':
# まだその部屋から回収していなければ
if not now_index in collected_index:
ans += A[now_index] # スコアを獲得
collected_index.add(now_index)
if c == 'B':
now_index = 0
print(ans)
投稿日時:
最終更新:
