Codeforces1313 C. Skyscrapers(单调队列 or RMQ)

本文探讨了在特定条件下,如何在一系列地块上建造摩天大楼,以最大化总楼层数量,同时遵循城市设计规范,避免形成左右两侧都有更高建筑的格局。通过使用单调队列维护峰值并选择最优方案,实现建筑布局的最优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description:

The outskirts of the capital are being actively built up in Berland. The company “Kernel Panic” manages the construction of a residential complex of skyscrapers in New Berlskva. All skyscrapers are built along the highway. It is known that the company has already bought n plots along the highway and is preparing to build n skyscrapers, one skyscraper per plot.

Architects must consider several requirements when planning a skyscraper. Firstly, since the land on each plot has different properties, each skyscraper has a limit on the largest number of floors it can have. Secondly, according to the design code of the city, it is unacceptable for a skyscraper to simultaneously have higher skyscrapers both to the left and to the right of it.

Formally, let’s number the plots from 111 to nnn. Then if the skyscraper on the i-th plot has aia_iai floors, it must hold that ai is at most mi(1≤ai≤mi)m_i (1≤a_i≤mi)mi(1aimi). Also there mustn’t be integers jjj and kkk such that j<i<kj<i<kj<i<k and aj>ai<aka_j>a_i<a_kaj>ai<ak. Plots jjj and kkk are not required to be adjacent to iii.

The company wants the total number of floors in the built skyscrapers to be as large as possible. Help it to choose the number of floors for each skyscraper in an optimal way, i.ei.ei.e. in such a way that all requirements are fulfilled, and among all such construction plans choose any plan with the maximum possible total number of floors.

Input

The first line contains a single integer n(1≤n≤500000)n (1≤n≤500000)n(1n500000) — the number of plots.

The second line contains the integers m1,m2,…,mn(1≤mi≤109)m_1,m_2,…,m_n (1≤m_i≤10^9)m1,m2,,mn(1mi109) — the limit on the number of floors for every possible number of floors for a skyscraper on each plot.

Output

Print nnn integers aia_iai — the number of floors in the plan for each skyscraper, such that all requirements are met, and the total number of floors in all skyscrapers is the maximum possible.

If there are multiple answers possible, print any of them.

Examples

input

5
1 2 3 2 1

output

1 2 3 2 1

input

3
10 6 8

output

10 6 6

Note

In the first example, you can build all skyscrapers with the highest possible height.

In the second test example, you cannot give the maximum height to all skyscrapers as this violates the design code restriction. The answer [10,6,6][10,6,6][10,6,6] is optimal. Note that the answer of [6,6,8][6,6,8][6,6,8] also satisfies all restrictions, but is not optimal.

题意:

给出一个长度为 nnn 的序列,把这个序列变成一个不存在对于任何一个元素两边都比他大的序列,就是类似一个倒 vvv,对于 C1C1C1 数据很小,怎么做都行。
复杂度更低的做法就是用单调队列来维护峰值,然后维护 nnn 个数以后再遍历一遍找到和最大的那个,具体操作可以看代码。

AC代码:

#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <cmath>
#include <map>
#include <set>
#include <string>
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <stack>
#include <queue>
using namespace std;
#define sd(n) scanf("%d", &n)
#define sdd(n, m) scanf("%d%d", &n, &m)
#define sddd(n, m, k) scanf("%d%d%d", &n, &m, &k)
#define pd(n) printf("%d\n", n)
#define pc(n) printf("%c", n)
#define pdd(n, m) printf("%d %d\n", n, m)
#define pld(n) printf("%lld\n", n)
#define pldd(n, m) printf("%lld %lld\n", n, m)
#define sld(n) scanf("%lld", &n)
#define sldd(n, m) scanf("%lld%lld", &n, &m)
#define slddd(n, m, k) scanf("%lld%lld%lld", &n, &m, &k)
#define sf(n) scanf("%lf", &n)
#define sc(n) scanf("%c", &n)
#define sff(n, m) scanf("%lf%lf", &n, &m)
#define sfff(n, m, k) scanf("%lf%lf%lf", &n, &m, &k)
#define ss(str) scanf("%s", str)
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, n, a) for (int i = n; i >= a; i--)
#define mem(a, n) memset(a, n, sizeof(a))
#define debug(x) cout << #x << ": " << x << endl
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define fi first
#define se second
#define mod(x) ((x) % MOD)
#define gcd(a, b) __gcd(a, b)
#define lowbit(x) (x & -x)
typedef pair<int, int> PII;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
inline int read()
{
    int ret = 0, sgn = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            sgn = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        ret = ret * 10 + ch - '0';
        ch = getchar();
    }
    return ret * sgn;
}
inline void Out(int a) //Êä³öÍâ¹Ò
{
    if (a > 9)
        Out(a / 10);
    putchar(a % 10 + '0');
}

ll gcd(ll a, ll b)
{
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b)
{
    return a * b / gcd(a, b);
}
///快速幂m^k%mod
ll qpow(ll x, ll n, ll mod)
{
    if (n == 0)
        return 1;
    ll res = qpow((x * x) % mod, n / 2, mod) % mod;
    if (n & 1)
        res = (res * x) % mod;
    return res % mod;
}
// 快速幂求逆元
int Fermat(int a, int p) //费马求a关于b的逆元
{
    return qpow(a, p - 2, p);
}

///扩展欧几里得
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if (b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    ll g = exgcd(b, a % b, x, y);
    ll t = x;
    x = y;
    y = t - a / b * y;
    return g;
}

const int N = 500010;
ll a[N], b[N], x[N], y[N], ans, sum;
int tmp, n, pos = 0, res[N];

int main()
{
    sd(n);
    pos = 0;
    rep(i, 1, n)
    {
        sld(a[i]);
        b[i] = a[i];
    }
    sum = pos = 0;
    rep(i, 1, n)
    {
        while (pos && b[i] <= b[res[pos]])
        {
            sum -= 1ll * (res[pos] - res[pos - 1]) * b[res[pos]];
            pos--;
        }
        res[++pos] = i;
        sum += 1ll * (res[pos] - res[pos - 1]) * b[i];
        x[i] = sum;
    }
    for (int i = 1; i * 2 <= n; ++i)
    {
        swap(b[i], b[n - i + 1]);
    }
    sum = pos = 0;
    rep(i, 1, n)
    {
        while (pos && b[i] <= b[res[pos]])
        {
            sum -= 1ll * (res[pos] - res[pos - 1]) * b[res[pos]];
            pos--;
        }
        res[++pos] = i;
        sum += 1ll * (res[pos] - res[pos - 1]) * b[i];
        y[i] = sum;
    }
    rep(i, 1, n)
    {
        if (ans < x[i] + y[n - i + 1] - a[i])
        {
            ans = x[i] + y[n - i + 1] - a[i];
            tmp = i;
        }
    }
    per(i, tmp - 1, 1)
    {
        a[i] = min(a[i], a[i + 1]);
    }
    rep(i, tmp + 1, n)
    {
        a[i] = min(a[i], a[i - 1]);
    }
    rep(i, 1, n)
        printf("%lld ", a[i]);
    printf("\n");
    return 0;
}

下面学习了一种更好理解的RMQ做法,整体思路是一样的,就是维护起来比较简单。

先RMQ处理数据,对于每一个位置,二分找里当前位置最近的那个小于当前位置值的位置,比如53454,对最后一个4,就是找到3,就是把53454,变成53444,第一个比自己小到自己中间一段变得和自己一样。

prei是以i为最高点让1-i递增的最小耗费,suf是以i为最高点让i-n递减的最小耗费。
然后遍历一遍求出以每个位置作为峰值的最小花费即可。

AC代码:

const int N = 500010;
int n, x, y;
ll a[N];
ll dp[N][25];
ll pre[N], suf[N];
void rmq_init()
{
    for (int i = 1; i <= n; i++)
        dp[i][0] = a[i];
    for (int j = 1; (1 << j) <= n; j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            dp[i][j] = min(dp[i][j - 1], dp[i + (1 << j - 1)][j - 1]);
}
ll rmq_query(int l, int r)
{
    int k = log2(r - l + 1);
    return min(dp[l][k], dp[r - (1 << k) + 1][k]);
}

void solve(ll t[])
{
    t[1] = a[1];
    rep(i, 2, n)
    {
        if (rmq_query(1, i - 1) >= a[i])
            t[i] = i * a[i]; //前面的都比当前位置的高,就变成当前位置这个高度。
        else
        {
            int l = 1, r = i - 1, ans, mid;
            while (l <= r)
            {
                mid = (l + r) >> 1;
                if (rmq_query(mid, r) < a[i]) //右半区间存在比当前位置低的
                    l = mid + 1, ans = mid;
                else
                    r = mid - 1;
            }
            t[i] = (i - ans) * a[i] + t[ans];
        }
    }
}
int main()
{
    sd(n);
    rep(i, 1, n)
        sld(a[i]);
    rmq_init();
    solve(pre);
    reverse(a + 1, a + 1 + n);
    rmq_init();
    solve(suf);
    int id;
    ll ans = 0;
    reverse(a + 1, a + 1 + n);
    rmq_init();
    rep(i, 1, n)
    {
        if (pre[i] + suf[n - i + 1] - a[i] > ans)
        {
            ans = pre[i] + suf[n - i + 1] - a[i];
            id = i;
        }
    }
    rep(i, 1, id)
    {
        printf("%lld ", rmq_query(i, id));
    }
    vector<ll> rec;
    per(i, n, id + 1)
        rec.pb(rmq_query(id, i));
    int len = rec.size();
    per(i, len - 1, 0)
        printf("%lld ", rec[i]);
    puts("");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值