mayoko’s diary

プロコンとかいろいろ。

CODE FESTIVAL 2014 予選B D - 登山家

解法

stack でがんばります。

まず, この問題では「右側に見える範囲」及び「左側に見える範囲」を別々に求められれば良いことがわかります。ということで, 右側に見える範囲を求めてみましょう。それがわかれば左側に見える範囲は配列 h を reverse させるだけで出来ます。

山の高さが非増加の間はどんどん山の高さ, 座標を stack に入れ込んでいきます。増加してしまったら, その座標を i として, stack の中で h[i] より小さいものは stack から削除していって, 右側に見える範囲を確定します。

const int MAXN = 100010;
int N;
int h[MAXN], ans[MAXN];

void calc() {
    stack<pii> S;
    int cur = 0;
    while (cur < N) {
        S.push(pii(h[cur], cur));
        int i = cur+1;
        while (S.top().first >= h[i] && i < N) {
            S.push(pii(h[i], i));
            i++;
        }
        while (!S.empty() && S.top().first < h[i]) {
            auto p = S.top();
            ans[p.second] += i-1-p.second;
            S.pop();
        }
        cur = i;
    }
    while (!S.empty()) {
        auto p = S.top();
        ans[p.second] += N-1-p.second;
        S.pop();
    }
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> h[i];
    }
    calc();
    reverse(h, h+N);
    reverse(ans, ans+N);
    calc();
    reverse(ans, ans+N);
    for (int i = 0; i < N; i++) cout << ans[i] << endl;
    return 0;
}