💻

ABC371-E DP解法

2024/09/17に公開

公式解説では主客転倒 + 余事象を使った解き方が紹介されていましたが、少し違うアプローチ(分類するならDP?)で解いたのでそれをまとめておきたいと思います。

問題

https://atcoder.jp/contests/abc371/tasks/abc371_e

問題概要

長さ Nの整数列A=(A_{1}, \dots, A_{N})に対し、f(l, r) \ (1\leq l \leq r \leq N)を、「集合\{A_{l}, A_{l+1}, \dots, A_{r-1}, A_{r}\} の要素数」と定める。このとき

\sum_{i=1}^{N}\sum_{j=i}^{N}f(i, j)
を求めよ。

制約

  • 1\leq N \leq 2\times 10^{5}
  • 1\leq A_{i} \leq N

解法

以下ではAの添え字を一つずらして0-indexed で考えます。

方針

g(i):=\sum_{j=i}^{N}f(i, j) と書くことにします。最終的に求めるものはg(0) + \cdots + g(N-1)です。ここで

  1. g(0) の計算はO(N) で可能。
  2. よって、各1\leq i \leq N-1について、g(i-1)からg(i) を高速に求められれば良い。
  3. g(i)g(i-1)の差は、A_{i-1}と、A_{k}=A_{i-1} \ (k\geq i)なるkの情報が分かれば良い。
    ということに着目しました。

詳細

まず\text{appear}[i][x]を、「i\leq k \leq N-1かつA_{k} = xとなるk」を昇順に並べたリストとします。
例えば、N=3, A=(1, 2, 2) (問題の入力例1)の場合は次の表のようになります:

i \setminus x 1 2
0 [1] [2, 3]
1 [] [2, 3]
2 [] [3]

このとき、1\leq i\leq N-1について次のことが成り立ちます:

  1. \text{appear}[i][A_{i-1}] が空のとき、g(i) = g(i-1) - (N-i+1).
  2. \text{appear}[i][A_{i-1}] が空でないとき、g(i) = g(i-1) - (\text{appear}[i][A_{i-1}][0]-i+1).
証明

まず1について、\text{appear}[i][A_{i-1}] が空であるとします。この場合、j\geq iであればA_{j} \ne A_{i-1} が成り立ちます。よって、各j = i, i+1, \dots, N-1について、

\{A_{i-1}, A_{i}, A_{i+1}, \dots, A_{j}\} = \{A_{i-1}\} \sqcup \{A_{i}, A_{i+1}, \dots, A_{j}\}

が成り立ちます[1]。よって、

\begin{aligned} g(i) &= \sum_{j=i}^{N-1}\left(\text{集合}\{A_{i}, A_{i+1}, \dots A_{j}\}\text{の要素数}\right) \\ &= \sum_{j=i}^{N-1}\left(\text{集合}\{A_{i-1}, A_{i}, A_{i+1}, \dots A_{j}\}\text{の要素数} - 1\right) \\ &= \sum_{j=i}^{N-1}\left(\text{集合}\{A_{i-1}, A_{i}, A_{i+1}, \dots A_{j}\}\text{の要素数}\right) - (N-i) \\ &= \sum_{j=i-1}^{N-1}\left(\text{集合}\{A_{i-1}, A_{i}, A_{i+1}, \dots A_{j}\}\text{の要素数}\right) - \left(\text{集合}\{A_{i-1}\}\text{の要素数}\right) - (N-i) \\ &= g(i-1) - (N-i+1) \end{aligned}

が成り立ちます。

2についても考え方は同様です。\text{appear}[i][A_{i-1}] が空でないとします。このとき、\text{appear}[i][A_{i-1}][0]A_{k}=A_{i-1}かつk\geq iを満たす最小のkであることから、K:=\text{appear}[i][A_{i-1}][0] とすると、

\begin{aligned} i\leq j < K &\Rightarrow \{A_{i-1}, A_{i}, A_{i+1}, \dots, A_{j}\} = \{A_{i-1}\} \sqcup \{A_{i}, A_{i+1}, \dots, A_{j}\}, \\ K \leq j \leq N-1 &\Rightarrow \{A_{i-1}, A_{i}, A_{i+1}, \dots, A_{j}\} = \{A_{i}, A_{i+1}, \dots, A_{j}\} \end{aligned}

が成り立ちます。従って、

\begin{aligned} g(i) &= \sum_{j=i}^{N-1}\left(\text{集合}\{A_{i}, A_{i+1}, \dots A_{j}\}\text{の要素数}\right) \\ &= \sum_{j=i}^{K-1}\left(\text{集合}\{A_{i}, A_{i+1}, \dots A_{j}\}\text{の要素数}\right) \\ & \ \ \ \ + \sum_{j=K}^{N-1}\left(\text{集合}\{A_{i}, A_{i+1}, \dots A_{j}\}\text{の要素数}\right)\\ &= \sum_{j=i}^{K-1}\left(\text{集合}\{A_{i-1}, A_{i}, A_{i+1}, \dots A_{j}\}\text{の要素数} - 1\right) \\ & \ \ \ \ + \sum_{j=i}^{K-1}\left(\text{集合}\{A_{i-1}, A_{i}, A_{i+1}, \dots A_{j}\}\text{の要素数}\right) \\ &= \sum_{j=i}^{N-1}\left(\text{集合}\{A_{i-1}, A_{i}, A_{i+1}, \dots A_{j}\}\text{の要素数}\right) - (K-i) \\ &= \sum_{j=i-1}^{N-1}\left(\text{集合}\{A_{i-1}, A_{i}, A_{i+1}, \dots A_{j}\}\text{の要素数}\right) - \left(\text{集合}\{A_{i-1}\}\text{の要素数}\right) - (K-i) \\ &= g(i-1) - (K-i+1) \\ &= g(i-1) - (\text{appear}[i][A_{i-1}][0]-i+1) \end{aligned}

が成り立ちます。

よってg(i-1), \text{appear}[i][A_{i-1}] が分かっていれば、g(i)O(1)で求まります。

今、\text{appear}[0]O(N)で求めることができます。また、

\text{appear}[i][x] = \begin{cases} \text{appear}[i-1][x], &\text{if } x \ne A_{i-1} \\ \text{appear}[i-1][x] \text{から先頭の要素を削除したもの}, &\text{if } x = A_{i-1} \end{cases}

なので、in-place に更新することで\text{appear}[i-1]から\text{appear}[i-1]O(1)で求まります。

以上から、g(0), g(1), \dots g(N-1)O(N)で求まり、最終的な答えである

g(0)+g(1)+\dots +g(N-1)
O(N)で求まります。

実装

Python での実装(コンテスト後にこの記事に合わせて少し書き換えたもの)です。
https://atcoder.jp/contests/abc371/submissions/57850582

(ほとんど同じではありますが、コンテスト中の実装よりはよくなっていると思います。)

脚注
  1. 2つの集合B, Cが共通部分を持たないとき、BCの和集合をB\sqcup Cと書いています。ここでは\{A_{i-1}\} \cup \{A_{i}, A_{i+1}, \dots, A_{j}\}と書いても同じ意味ですが、\{A_{i-1}\} \cap \{A_{i}, A_{i+1}, \dots, A_{j}\} = \emptysetであることを強調しています。 ↩︎

Discussion