全国統一プログラミング王決定戦本戦 D問題:Deforestation

問題. Deforestation

1 から  n までの番号付けされた  n 本の竹がある.時刻 0 において全ての竹の高さは 0 で,時刻が 1 経過するごとに各竹の高さが 1 伸びる. m 回のイベントがあり, i 番目のイベントでは時刻  t_i に番号が  l_i 以上  r_i 以下の竹を伐採する.このときに伐採された竹の高さの総和がそのイベントの得点となる. m 回のイベントで得られる得点の総和を求めよ.

制約 1 \le n, m \le 2 \times 10^5,  1 \le t_1 < t_2 < \ldots < t_m \le 10^9
 

解法1.セグメント木(区間更新点クエリ)

 i 番目の竹が伐採されるイベントの時刻を  t_{i_1} < t_{i_2} < \ldots < t_{i_k} とする.このとき,各イベントで得られる得点は, t_{i_1}, t_{i_2} - t_{i_1}, t_{i_3} - t_{i_2}, \ldots, t_{i_k} - t_{i_{k - 1}} となる. i 番目の竹が最終得点に寄与する部分は,
   (t_{i_1}) + (t_{i_2} - t_{i_1}) + (t_{i_3} - t_{i_2}) + \cdots + ( t_{i_k} - t_{i_{k - 1}}) = t_{i_k}
となるので,最終的に得られる得点の総和は,各竹が伐採される最も遅い時刻の総和である.
各竹に対して伐採される最も遅い時刻をセグメント木の値として持ち,各イベントでその値を区間で更新する.すべてのイベント終了後のセグメント木の値の総和が答えとなる.

計算時間 O(m \log n + n \log n)

 

解法2. set 関数

イベントを逆順で行うことを考える.初めて伐採される竹はそのイベントでの時刻が得点となり,それ以降のイベントでは答えに寄与することはない.したがって, i 番目のイベントで得られる得点は,まだ伐採されていない  l_i 以上  r_i 以下の番号の竹の数を  n_i とすると,  t_i \times n_i となる.C++set の要素をまだ伐採されていない竹の番号とすることによってこのクエリを高速に行うことができる.

計算時間 O(n \log n + m \log n)

 

set.erase(l, r) って便利ですね.