Google Code Jam 2019 Round1B : Fair Fight

問題. Fair Fight

非負整数  K と2つの非負整数列  (c_0, c_1, \ldots, c_{N - 1}), \, (d_0, d_1, \ldots, d_{N - 1}) が与えられる.組  (l, r) \, (0 \le l \le r \le N - 1) \left| \max(c_l, c_{l + 1}, \ldots, c_{r}) - \max(d_{l}, d_{l + 1}, \ldots, d_{r}) \right| \le K を満たすものがいくつあるかを求めよ.

制約 0 \le K, N \le 10^5,  0 \le c_i, d_i \le 10^5

解法.二分探索 + RMQ

解法が思いつかなかったので 解説 を参考にした.


任意の  i \,(0 \le i < N) に対して  c_i が最大値として選ばれて,かつ,条件を満たす区間の数を  f(i) とする.ただし,最大値が複数ある場合は添字が最も小さいものが選ばれるものとする.関数  f が計算できると答えは  \sum_{i = 0}^{N - 1} f(i) となるので  f を求めることを考える.

 f(i) の値に寄与する区間 [L, R] とすると, [L, R] は次の条件を満たす.
 条件1.  \max(c_{L}, c_{L + 1}, \ldots, c_{i - 1}) < c_{i}
 条件2.  \max(c_{i + 1}, c_{i + 2}, \ldots, c_{R}) \le c_{i}
 条件3.  \max(d_L, d_{L + 1}, \cdots, d_{R}) \le c_{i} + K
 条件4.  \max(d_L, d_{L + 1}, \cdots, d_{R}) \ge c_{i} - K
条件1, 2は  c_i が最大値として選ばれているという条件で,条件2, 3は問題文にある制約の絶対値を書き直したものである.観察として,区間  [L, R] の部分区間  [L', R'] \, (L \le L' \le R' \le R) f(i) の値に寄与する区間である.したがって, [L, R] が上の条件を満たす極大な区間のとき,
  f(i) = (i - L + 1) \times (R - i + 1)
となる.

 \left|c_i - d_i \right| > K の場合は  f(i) = 0 となるので  \left| c_i - d_i \right| \le K と仮定する.
まず初めに条件1を満たす区間の左端で最も左にあるものを求める.上の観察より,左端を  l としたときに条件  \max(c_{l}, c_{l + 1}, \ldots, c_{i - 1}) < c_{i} を満たすかどうかという性質は単調になるので  l に関する二分探索を行う.各  l に対する最大値関数の計算は  c のRange Maximum Query に答える関数  RMQ_c (l, r) = \min(c_l, c_{l + 1}, \ldots, c_{r - 1}) を使用する. RMQ_c区間木で実装すると,前処理に  O(N \log N) 時間,各クエリに  O(\log N) 時間かかるので,極大な左端を求めるのに全体で  O(\log^2 N) 時間かかる.しかし, c の値は変更しないので Sparse Table で実装すると,前処理に  O(N \log N) 時間,各クエリに  O(1) 時間で全体で  O(\log N) 時間で計算可能である(実装では区間のみ を選択. Sparse Tableの実装を追加).同様に条件2を満たす極大な区間の右端も  O(\log^2 N) 時間で求まる.

次に,条件3, 4について考える.上で求めた条件1, 2を満たす極大な区間 [L, R] とする.条件3, 4を同時に満たす区間の数を数えるのは大変なので, [L, R] の部分区間で次の条件を満たす極大な区間をそれぞれ求める(条件3と3'は同じ).
 条件3'.  \max(d_l, d_{l + 1}, \cdots, d_{r}) \le c_{i} + K
 条件4'.  \max(d_l, d_{l + 1}, \cdots, d_{r}) < c_{i} - K
これは,上の方法と同様に  d の Range Maximum Query に答える関数  RMQ_d を求めて  l r をそれぞれ別に二分探索で求めると同じ計算時間で求まる.このとき,条件3'と条件4'を満たす極大な区間をそれぞれ  [l_3, r_3], \, [l_4, r_4] とすると,
  f(i) = (i - l_3 + 1) \times (r_3 - i + 1) - (i - l_4 + 1) \times (r_4 - i + 1)
となる.

計算時間 O(N \log^2 N)


計算時間 O(N \log N)

いろいろ細かいことが考えられないので大変だった.グローバル空間を汚したくないのでクラスで実装したけど,競プロだと実装時間も順位の要因になるので悩ましい.今後のGCJではテンプレートにして試してみる.
後で Sparse Table での実装をしてみる.構築に  O(n) 時間でできるのもあるらしいのでそれも(Range Minimum Query - Qiita).RmQの表記を使いたかったけど Maximum だった・・・.