CodeForces-1313C2スカイスクレイパー(ハードバージョン)
元のタイトルの住所:
http://codeforces.com/contest/1313/problem/C2
トピック:
各位置に設定できるサイズの上限を考えると、j <i <kおよびaj> ai <akを満たすために各位置の実際のサイズを設定する必要があります(jおよびkはiに隣接する必要はありません)。
つまり、左側で減少せず右側で増加しない中心点を見つけて、各点の実際のサイズを構成して合計を最大化します。
基本的なアイデア:
各位置iで間隔[1-i]を維持するために、最初に単調スタックが左から維持されます。非減少によって取得された最大値はl [i]に記録されます。同様に、右の維持間隔から[N-i]は非減少によって取得できます。の最大値はr [i]に記録されます。
次に、位置iが中心点のときに得られる実際の合計はr [i] + l [i] -a [i]であり、最大値が位置する位置を中心として、条件に応じて両側に対する答えを構成します。
参照コード:
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define int long long
#define INF 0x3f3f3f3f
const int maxn = 5e5+10;
int n,a[maxn],l[maxn],r[maxn],res[maxn];
signed main() {
IO;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
vector<int> q;
q.push_back(0);
for (int i = 1; i <= n; i++) {
while (!q.empty() && a[q.back()] >= a[i]) q.pop_back();
int t = q.back();
l[i] = l[t] + a[i] * (i - t);
q.push_back(i);
}
q.clear();
q.push_back(n+1);
for (int i = n; i >= 1; i--) {
while (!q.empty() && a[q.back()] >= a[i]) q.pop_back();
int t = q.back();
r[i] = r[t] + a[i] * (t - i);
q.push_back(i);
}
int ans = 0, pos = 0;
for (int i = 1; i <= n; i++) {
int temp = l[i] + r[i] - a[i];
if (temp > ans) {
ans = temp, pos = i;
}
}
res[pos] = a[pos];
for (int i = pos - 1; i >= 1; i--) res[i] = min(a[i], res[i + 1]);
for (int i = pos + 1; i <= n; i++) res[i] = min(a[i], res[i - 1]);
for (int i = 1; i <= n; i++) cout << res[i] << " ";
cout << endl;
return 0;
}