Processing math: 100%

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

G - Alone

問題

  • 長さ N の数列 A=(A1,...,AN) が与えられる
  • A の長さ1以上の区間 [l,r) であって、「区間内に1回しか現れない数字がある」ものの個数を数えよ
  • 2N2×105

解法

左端 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つずらすと、「Al について」(例の場合は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 の候補は Nl 個ある。
セグ木の [l,N) の最小値が0なら、Nl から0の個数を除いたもの、最小値が正なら Nl 全てが答えとなる。

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