• C2. Skyscrapers (线段树 + 分治 || 单调栈 + 思维)


    题目: 传送门

    题意: 你需要建 n 栋楼,第 i 栋楼最多能建 m[ i ] 层,然后你需要构造一个数组 a 使得对任意 i  满足 1 <= a[ i ] <= m[ i ], 且不能存在 j < i < k and a[ j ] > a[ i ] < a[ k ],也就是说不能同时存在 j 和 k 使得 a[ j ] > a[ i ] 且 a[ i ] < a[ k ],然后你得使得最后建好的 n 栋楼的总楼层最大,输出 a 数组。

     1 <= n <= 500000, 1 <= m[ i ] <= 1e9

    题解: 

    解法一: 

      首先很容易想到分治的算法,我们先求区间 [ 1, n ] 里的最小值的位置,那么根据题意的原则,要么这个最小值的左边都得变成这个最小值, 要么右边都得变成这个值。那我们就可以分治分别求出左边跟右边,然后取最优的即可。然后区间最小值可以用线段树维护,这样最终的复杂度就是 nlogn。

    解法二:

      我们可以定义 pres[ i ] 为区间 [ 1, i ] 能建总楼层的最大值。然后,我们考虑怎么求这个数组。若 a[ j ] 是 1 ~ i - 1 最后一个小于等于 a[ i ] 的数,那么 j + 1 ~ i 这些楼最多只能建 a[ i ],那么 pres[ i ] = pre[ j ] + (j - i ) * a[ i ]。然后,关于求 1 ~ i - 1 最后一个小于等于 a[ i ] 的数可以用单调栈维护。然后再定义 nxs[ i ] 为区间 [ i, n ] 能建总楼层的最大值。求法和 pres 数组一样。然后,还要维护一下,那些比 a[ i ] 小的数的位置,具体可看代码。那么最后就是,遍历一遍,求最大值 pres[ i ] + nxs[ i ] - a[ i ] 的位置,然后算答案即可。

    解法一

    #include <bits/stdc++.h>
    #define LL long long
    #define mem(i, j) memset(i, j, sizeof(i))
    #define rep(i, j, k) for(int i = j; i <= k; i++)
    #define dep(i, j, k) for(int i = k; i >= j; i--)
    #define pb push_back
    #define make make_pair
    #define INF INT_MAX
    #define inf LLONG_MAX
    #define PI acos(-1)
    using namespace std;
    
    const int N = 1e6 + 5;
    int a[N], ans[N];
    int n;
    pair < int, int > t[N << 2];
    void build(int rt, int l, int r) {
        if(l == r) {
            t[rt] = make(a[l], l);
            return ;
        }
        int mid = (l + r) >> 1;
        build(rt << 1, l, mid);
        build(rt << 1 | 1, mid + 1, r);
        if(t[rt << 1].first <= t[rt << 1 | 1].first) t[rt] = t[rt << 1];
        else t[rt] = t[rt << 1 | 1];
    }
    
    pair < int, int > query(int rt, int l, int r, int L, int R) {
        if(L <= l && r <= R) return t[rt];
        int mid = (l + r) >> 1;
        pair < int, int >  ans = make(INF, 0);
        if(L <= mid) {
            pair < int, int > tmp = query(rt << 1, l, mid, L, R);
            if(tmp.first < ans.first) ans = tmp;
        }
        if(R > mid) {
            pair < int, int > tmp = query(rt << 1 | 1, mid + 1, r, L, R);
            if(tmp.first < ans.first) ans = tmp;
        }
        return ans;
    }
    
    LL solve(int l, int r) {
        if(l == r) {
            ans[l] = a[l];
            return 1LL * a[l];
        }
        if(l > r) return 0LL;
        pair < int, int > tmp = query(1, 1, n, l, r);
        int pos = tmp.second;
        LL ans1 = solve(l, pos - 1) + 1LL * (r - pos + 1) * a[pos];
        LL ans2 = solve(pos + 1, r) + 1LL * (pos - l + 1) * a[pos];
    //    cout << ans1 << " " << ans2 << " " << pos << endl;
        if(ans1 >= ans2) {
            rep(i, pos, r) ans[i] = tmp.first;
            return ans1;
        }
        else {
            rep(i, l, pos) ans[i] = tmp.first;
            return ans2;
        }
    }
    
    int main() {
        scanf("%d", &n);
        rep(i, 1, n) scanf("%d", &a[i]);
        build(1, 1, n);
        solve(1, n);
        rep(i, 1, n) printf("%d ", ans[i]);
        return 0;
    }

    解法二

    #include <bits/stdc++.h>
    #define LL long long
    #define mem(i, j) memset(i, j, sizeof(i))
    #define rep(i, j, k) for(int i = j; i <= k; i++)
    #define dep(i, j, k) for(int i = k; i >= j; i--)
    #define pb push_back
    #define make make_pair
    #define INF INT_MAX
    #define inf LLONG_MAX
    #define PI acos(-1)
    using namespace std;
    
    const int N = 1e6 + 5;
    int a[N], pre[N], nx[N], s[N], tot, ans[N];
    LL pres[N], nxs[N];
    int main() {
        int n; scanf("%d", &n);
        rep(i, 1, n) scanf("%d", &a[i]);
        rep(i, 1, n) {
            while(tot && a[i] < a[s[tot]]) tot--;
            pre[i] = s[tot];
            pres[i] = pres[s[tot]] + 1LL * (i - s[tot]) * a[i];
            s[++tot] = i;
        }
        tot = 0; s[0] = n + 1;
        dep(i, 1, n) {
            while(tot && a[i] < a[s[tot]]) tot--;
            nx[i] = s[tot];
            nxs[i] = nxs[s[tot]] + 1LL * (s[tot] - i) * a[i];
            s[++tot] = i;
        }
        LL ma = 0LL; int pos = -1;
        rep(i, 1, n) {
            if(pres[i] + nxs[i] - 1LL * a[i] > ma) {
                ma = pres[i] + nxs[i] - 1LL * a[i];
                pos = i;
            }
        }
        for(int i = pos; i; i = pre[i]) {
            rep(j, pre[i] + 1, i) ans[j] = a[i];
        }
        for(int i = pos; i <= n ; i = nx[i]) {
            rep(j, i, nx[i] - 1) ans[j] = a[i];
        }
        rep(i, 1, n) printf("%d ", ans[i]);
        return 0;
    }
    一步一步,永不停息
  • 相关阅读:
    服务器端渲染和客户端渲染
    混沌开窍---24幅由算法生成的正方形图像
    三生万物---算法生成的25幅三角形图像
    九尾之火---算法生成的动画图像
    最简单的分形图像生成算法
    正与邪---25幅由算法生成的五角星图像
    一幅画<十六芒星盾>---程序员or艺术家
    火火火---12幅算法生成火的图像
    恶魔的指纹---49幅由算法生成的七芒星图像
    混沌分形之电子云
  • 原文地址:https://www.cnblogs.com/Willems/p/12359272.html
Copyright © 2020-2023  润新知