ユニークビジョンプログラミングコンテスト2024 春(AtCoder Beginner Contest 346)G問題メモ

G - Alone

問題

  • 長さ $N$ の数列 $A=(A_1,...,A_N)$ が与えられる
  • $A$ の長さ1以上の区間 $[l,r)$ であって、「区間内に1回しか現れない数字がある」ものの個数を数えよ
  • $2 \le N \le 2 \times 10^5$

解法

左端 $l$ を固定して考えてみると、$A$ に出てくるそれぞれの数字について、 「$l$ 以降、1個目が出てくる箇所から、2個目が出てくる箇所の手前」までが $r$ だったら、その数字が1回しか現れないことになる。

         l
i  0  1  2  3  4  6  7  8  9 10 11
A  1  2  3  2  3  3  2  3  1  2  1

         |---->                       3について
            |------->                 2について
                           |---->     1について
         o  o  o  o  x  x  o  o  x    区間右端(r-1)にしてよい場所

ただし、異なる数字で区間(|—→)が重なっていても、カウントするのは1回である。

$l$ を1つずらすと、「$A_l$ について」(例の場合は3について)の区間が1つ先にずれる。他はそのまま。

            l
i  0  1  2  3  4  6  7  8  9 10 11
A  1  2  3  2  3  3  2  3  1  2  1

               |->                    3について[New!]
            |------->                 2について
                           |---->     1について
            o  o  o  x  x  o  o  x    区間右端(r-1)にしてよい場所

               l
i  0  1  2  3  4  6  7  8  9 10 11
A  1  2  3  2  3  3  2  3  1  2  1

               |->                    3について
                     |------->        2について[New!]
                           |---->     1について
               o  x  o  o  o  o  x    区間右端(r-1)にしてよい場所

なので、数字毎に出現indexを整理しておき、「区間に1を加算する」「区間に1を加算してたのを戻す(-1を加算する)」「区間内の正の数の個数を得る」ということができれば、左から順にそこを $l$ にした時の答えを求めていける。

これは区間作用・区間集約なので、遅延評価セグメント木で実現できそうなのだが、そのまま「今の加算された値」を持たせると上手くいかない。
少し頭をひねる必要がある。

データに「その区間の最小値」と「最小値の個数」を持たせる。(作用素は普通に、加算値でよい)

  • 区間全体に1を足す→その区間の最小値が+1される
  • 2つの区間をマージする→最小値が同じなら個数も加算する。違うなら小さい方を継承する。

こうすると、「全体の最小値が0か? 0の場合は何個あるか?」がわかる。
今回の場合、0か正かが区別できればよく、負にはならないという性質を利用し、最小値だけ管理することで実現できる。

$l$ を固定した時、$r$ の候補は $N-l$ 個ある。
セグ木の $[l, N)$ の最小値が0なら、$N-l$ から0の個数を除いたもの、最小値が正なら $N-l$ 全てが答えとなる。

Python3

programming_algorithm/contest_history/atcoder/2024/0323_abc346.txt · 最終更新: 2024/03/27 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0