Official

E - 山並みの眺望 / View of the Mountain Range Editorial by physics0523


この問題は、 スライド最小値 とよく似た考え方で解くことができます。

山を左から辿っていくことを考えます。この時、今見ている山から見える山の集合 \(S\) をうまく管理することを考えます。 \(S\) は山の標高の配列(を降順に並べたもの)として保持しておきます。

  • 最初、 \(S\) は空である。
  • \(i=1,2,\dots,N\) について、以下を繰り返す。
    • この時点で、 \(S\) は山 \(i-1\) から見えている山の集合であることに留意する。
    • \(S\) が空でなく、 \(S\) の末尾が \(H_i\) 以下である限り、以下を繰り返す。
      • \(S\) の末尾は \(H_i\) 以下なので、問題文中の条件 \(1\) より、山 \(i\) から見えることはない。また、条件 \(2\) より、山 \(i+1,i+2,\dots,N\) からこの山が見えることもない。
    • この時点で \(S\) に要素が残っていれば、それらの山は条件 \(1,2\) 双方を満たしているので、山 \(i\) から見える。よって、答えにこの時点での \(|S|\) を加算する。
    • \(S\) の末尾に \(H_i\) を追加する。
      • この追加があっても、 \(S\) は標高が降順に並んだ列であることを確認されたい。

全体で時間計算量 \(O(N)\) でこの問題に正解できます。

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;
using ll=long long;

int main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  ll n;
  cin >> n;
  ll res=0;
  vector<ll> a;
  for(ll i=0;i<n;i++){
    ll h;
    cin >> h;
    while(!a.empty()){
      if(a.back()<=h){a.pop_back();}
      else{break;}
    }
    res+=a.size();
    a.push_back(h);
  }
  cout << res << "\n";
  return 0;
}