差分更新系の代表的問題。でも頭がごっちゃになって大変だった。
問題概要
'D', 'M', 'C' からなる長さ の文字列 と正の整数 が与えられる。 の添字 であって
- S[ ] = 'D'
- S[ ] = 'M'
- S[ ] = 'C'
を満たすものの個数を求めよ (というクエリに 回答えよ...各クエリは が指定される)。
制約
考えたこと
一瞬、苦手なクエリ処理系かと思ったけど、 というのを見た時点で、なんか変な高速化解法を封じるためなのかな...となった。あまりクエリ形式は本質でなさそうと。であれば問題ない!!!
で、各クエリに で答えられれば問題ない!!!僕の苦手なクエリ系は、最初は で前処理してもいいけどその後は や が要求されるようなやつ。今回のはそうじゃない。
各クエリ
というわけなので、実質 が与えられて な線形処理を目指す問題として解くことにする。こういう と添字が 3 つあるタイプの問題では、真ん中を固定するのが定石なのだが...今回はそれだと という条件と相性が悪いので、今回は、とりあえず の位置 を固定して考える。
そうしたときに として満たすものは
- を満たすような各 (S[ ] = 'C') に対して
- と の間に挟まっている 'M' の個数を合計したもの
になることがわかる。この値は、差分更新によって求められそうである。一般に、
- 'M' の個数 ()
- 'C' の個数 ()
- 'M', 'C' のこの順になっているペアの個数 ()
を管理することにする。よくある定石として
を考えて更新する、を考えるとわかりやすい。
区間の右側を進める
進めたことで追加される文字 c を考える
- c = 'M' のとき、++m
- c = 'C' のとき、++c, t += m
区間の左側を進める
進めたことで削除される文字 c を考える
- c = 'M' のとき、--m, t -= c
- c = 'C' のとき、--c
#include <iostream> #include <string> using namespace std; int N; string S; long long m, c, t; void add(int i) { if (S[i] == 'M') ++m; else if (S[i] == 'C') ++c, t += m; } void erase(int i) { if (S[i] == 'M') --m, t -= c; else if (S[i] == 'C') --c; } long long solve(int K) { long long res = 0; m = 0; t = 0; int r = 0; for (int l = 0; l < N;) { while (r < N && r - l < K) add(r++); if (S[l] == 'D') res += t; erase(l++); } return res; } int main() { while (cin >> N >> S) { int Q; cin >> Q; for (int i = 0; i < Q; ++i) { int k; cin >> k; cout << solve(k) << endl; } } }