いかにも stack が登場しそうな問題!
問題概要
下の図のように、高さが の仕切りが等間隔に並んでいて、その間と両端の カ所のスペースを表す番号を とする。これらは幅が等しい。
今、スペース 0 に水を入れていく。1 個分のスペースについて、水位は 1 秒で 1 だけ上昇する量の水である。
このとき、各 について、スペース に水が入り始める瞬間の時刻 + 1 の値を求めよ。
制約
考えたこと
スペース に水が入り始めるのは、スペース の水位が になった瞬間である。その瞬間の水の量を求めればよい。今、次のように配列 dp
を定義しよう。
dp[i]
← スペース に水が入り始める瞬間の時刻
すると、下図のように となる最大の [tex j] () をとったとき、
dp[i] = dp[j] + H[i] * (i - j)
と表せる。なお、ここで としておく。
あとは、各 に対して、 となる最大の [tex j] () を求める必要がある。これはよく知られた有名問題で、stack を使って の計算量で求められる。詳しくは次の問題の解説(鉄則本にある)を参照。
全体として配列 dp
を求める計算量は と評価できる。
コード
#include <bits/stdc++.h> using namespace std; const long long INF = 1LL<<60; int main() { long long N; cin >> N; vector<long long> H(N + 1, INF); for (int i = 1; i <= N; i++) cin >> H[i]; // H[j] > H[i] となる最大の j を求める vector<long long> prev(N+ 1); vector<pair<long long, int>> stac; for (int i = 0; i <= N; i++) { while (!stac.empty() && stac.back().first <= H[i]) stac.pop_back(); if (stac.empty()) prev[i] = 0; else prev[i] = stac.back().second; stac.emplace_back(H[i], i); } // DP vector<long long> dp(N+1, 0); for (int i = 1; i <= N; i++) { dp[i] = dp[prev[i]] + H[i] * (i - prev[i]); cout << dp[i]+1 << " "; } cout << endl; }