Official

C - 山の稜線 / Mountain Ridgeline Editorial by admin

claude4.8opus-high

概要

各峰を「山型の頂点」とみなしたとき、その頂点を中心に左へは狭義単調増加、右へは狭義単調減少が続く最長の区間を求め、その中で標高差が \(K\) 以上になるものの最大の峰数を答える問題です。

考察

まず素朴に考えると、すべての区間 \([l, r]\) を試して「山型かどうか」「標高差が \(K\) 以上か」を判定する方法がありますが、区間は \(O(N^2)\) 個あり、\(N \leq 10^6\) では到底間に合いません。

ここで重要な観察をします。山型の区間は必ず「頂点」となる峰 \(k\) を 1 つ持ちます。そこで 頂点を固定して考える ことにします。

頂点 \(k\) を固定したとき、

  • 頂点から左側は \(H_l < H_{l+1} < \cdots < H_k\) となるよう、狭義単調増加が続く限り左へ伸ばせます
  • 頂点から右側は \(H_k > H_{k+1} > \cdots > H_r\) となるよう、狭義単調減少が続く限り右へ伸ばせます

このとき得られる「頂点 \(k\) を中心とする最大の山型区間」が、頂点 \(k\) に対する最良候補になります。理由は次の通りです。

頂点 \(k\) を含む山型区間では、

  • 最大値は常に \(H_k\)(頂点)です。
  • 最小値は両端 \(H_l, H_r\) のうち小さい方です(左は増加・右は減少なので両端が最も低い)。

区間を最大まで伸ばすと両端の標高は下がる(または変わらない)ため、標高差 \(H_k - \min(H_l, H_r)\) は最大化されます。さらに区間の長さも最大になります。つまり「最大まで伸ばした区間で標高差が \(K\) 未満なら、それより短い区間でも条件を満たさない」ことが言えるので、各頂点について最大の山型区間だけを調べれば十分です。

アルゴリズム

連続部分列の長さを前計算する典型テクニックを使います。

  • \(\text{up}[i]\):峰 \(i\) で終わる狭義単調増加列の長さ
    • \(H_{i-1} < H_i\) なら \(\text{up}[i] = \text{up}[i-1] + 1\)、そうでなければ \(1\)
  • \(\text{down}[i]\):峰 \(i\) から始まる狭義単調減少列の長さ
    • \(H_i > H_{i+1}\) なら \(\text{down}[i] = \text{down}[i+1] + 1\)、そうでなければ \(1\)

これらを使うと、峰 \(i\) を頂点とする最大の山型区間は

  • 左端 \(l = i - \text{up}[i] + 1\)
  • 右端 \(r = i + \text{down}[i] - 1\)
  • 長さ \(\text{up}[i] + \text{down}[i] - 1\)(頂点を二重に数えるので \(-1\)

と表せます。

\(i\) について、最大値 \(H_i\) と最小値 \(\min(H_l, H_r)\) の差が \(K\) 以上であれば、その長さを答えの候補として更新します。

具体例\(H = [1, 3, 5, 2, 4]\), \(K = 3\) を考えます。

  • \(i=2\)(値 \(5\))が頂点のとき、左へ \(1, 3, 5\)(増加)、右へ \(5, 2\)(減少)と伸びるので区間は \([1,3,5,2]\)、長さ \(4\)。最大値 \(5\)、両端は \(1\)\(2\) なので最小値 \(1\)、差は \(4 \geq 3\) で条件を満たします。

このように各頂点を \(O(1)\) で評価できます。

計算量

  • 時間計算量: \(O(N)\)(前計算も走査も線形)
  • 空間計算量: \(O(N)\)(配列 \(H, \text{up}, \text{down}\)

実装のポイント

  • 山型の定義は 狭義 の不等号です。\(H_i = H_{i+1}\) の箇所では増加列・減少列が途切れる(長さ \(1\) にリセットされる)ことに注意してください。等号で繋いでしまうと WA になります。

  • 区間内の最小値は、両端の値のうち小さい方を取れば十分です(頂点に向かって増加・減少するため、両端が最も低い)。わざわざ区間全体の最小値を計算する必要はありません。

  • \(N \leq 10^6\) と大きいため、入力は sys.stdin.buffer.read() でまとめて読み込むと高速です。

  • 長さ \(1\) の区間(峰 1 つ)も山型ですが、その場合は標高差 \(0\) なので \(K \geq 1\) の制約上、条件を満たすことはありません。撮影候補が存在しないときは初期値の \(0\) がそのまま出力されます。

    ソースコード

import sys

def main():
    data = sys.stdin.buffer.read().split()
    n = int(data[0]); k = int(data[1])
    H = [int(x) for x in data[2:2+n]]

    up = [1] * n
    for i in range(1, n):
        if H[i-1] < H[i]:
            up[i] = up[i-1] + 1

    down = [1] * n
    for i in range(n-2, -1, -1):
        if H[i] > H[i+1]:
            down[i] = down[i+1] + 1

    ans = 0
    for i in range(n):
        l = i - up[i] + 1
        r = i + down[i] - 1
        hl = H[l]
        hr = H[r]
        mn = hl if hl < hr else hr
        if H[i] - mn >= k:
            length = up[i] + down[i] - 1
            if length > ans:
                ans = length

    print(ans)

main()

この解説は claude4.8opus-high によって生成されました。

posted:
last update: