牛客小白月赛28 E-会当凌绝顶,一览众山小 线段树+二分暴力模拟

牛客小白月赛28 E-会当凌绝顶,一览众山小 线段树+二分暴力模拟


传送门: https://ac.nowcoder.com/acm/contest/16081/E

题意

  • 登 山 顺 序 不 一 定 从 左 到 右 , 是 按 照 给 出 山 峰 的 顺 序 登山顺序不一定从左到右,是按照给出山峰的顺序
  • 找 到 左 边 第 一 个 大 于 当 前 山 峰 的 山 峰 的 坐 标 , 修 改 它 找到左边第一个大于当前山峰的山峰的坐标,修改它
  • 如 果 右 边 没 有 大 于 当 前 山 峰 的 , 找 到 离 当 前 山 峰 最 近 的 最 矮 山 峰 , 修 改 它 如果右边没有大于当前山峰的,找到离当前山峰最近的最矮山峰,修改它

思路

首 先 顺 序 是 按 照 给 出 的 山 峰 顺 序 , 并 且 x 坐 标 过 大 , 但 是 点 数 只 有 2 e 5 , 所 以 需 要 离 散 化 。 首先顺序是按照给出的山峰顺序,并且x坐标过大,但是点数只有2e5,所以需要离散化。 x2e5

对 于 一 个 点 , 需 要 找 到 左 边 和 右 边 两 个 特 殊 要 求 的 山 峰 , 这 个 过 程 可 以 二 分 。 对于一个点,需要找到左边和右边两个特殊要求的山峰,这个过程可以二分。
并 且 是 区 间 查 询 , 所 以 需 要 线 段 树 维 护 , 维 护 区 间 最 大 值 和 最 小 值 即 可 。 并且是区间查询,所以需要线段树维护,维护区间最大值和最小值即可。 线

  • 左 边 : 假 设 当 前 是 第 i 座 山 , 映 射 过 去 的 位 置 是 p o s ( 离 散 化 ) , 二 分 [ 1 , p o s ] 区 间 , 找 到 最 近 的 一 个 大 于 当 前 高 度 的 山 左边:假设当前是第i座山,映射过去的位置是pos(离散化),二分[1,pos]区间,找到最近的一个大于当前高度的山 ipos()[1,pos]
  • 右 边 : 同 理 。 假 设 映 射 的 位 置 是 p o s , 先 求 出 区 间 [ p o s + 1 , n ] 的 最 大 值 判 断 需 不 需 要 修 改 。 若 是 需 要 , 则 二 分 [ p o s + 1 , n ] 区 间 , 找 到 最 近 的 一 个 最 小 值 右边:同理。假设映射的位置是pos,先求出区间[pos+1,n]的最大值判断需不需要修改。若是需要,则二分[pos+1,n] 区间,找到最近的一个最小值 pos,[pos+1,n][pos+1,n]

Code

#include "bits/stdc++.h"
using namespace std;

const int N = 2e5 + 10;

struct node {
    int x, h;
}a[N];
int b[N], h[N], idx[N];

#define lc u << 1
#define rc u << 1 | 1
#define mid (t[u].l + t[u].r) / 2

struct Tree {
    int l, r;
    int mx, mn;
}t[N << 2];

inline void push_up(int u) {
    t[u].mx = max(t[lc].mx, t[rc].mx);
    t[u].mn = min(t[lc].mn, t[rc].mn);
}

void build(int u, int l, int r) {
    t[u].l = l; t[u].r = r;
    if(l == r) {
        t[u].mx = t[u].mn = h[l];
        return ;
    }
    int m = (l + r) / 2;
    build(lc, l, m);
    build(rc, m + 1, r);
    push_up(u);
}

void modify(int u, int p, int v) {
    if(t[u].l == t[u].r) {
        t[u].mn = t[u].mx = v;
        return ;
    }
    if(p <= mid) modify(lc, p, v);
    else modify(rc, p, v);
    push_up(u);
}

int query_max(int u, int ql, int qr) {
    if(ql <= t[u].l && t[u].r <= qr) return t[u].mx;
    int mx = -1e9 - 7;
    if(ql <= mid) mx = max(mx, query_max(lc, ql, qr));
    if(qr > mid)  mx = max(mx, query_max(rc, ql, qr));
    return mx;
}

int query_min(int u, int ql, int qr) {
    if(ql <= t[u].l && t[u].r <= qr) return t[u].mn;
    int mn = 1e9 + 7;
    if(ql <= mid) mn = min(mn, query_min(lc, ql, qr));
    if(qr > mid)  mn = min(mn, query_min(rc, ql, qr));
    return mn;
}

void solve() {
    int n; cin >> n;
    for(int i = 1;i <= n; i++) {
        cin >> a[i].x >> a[i].h;
        b[i] = a[i].x;
    }
    sort(b + 1, b + n + 1);
    for(int i = 1;i <= n; i++) {
        idx[i] = lower_bound(b + 1, b + n + 1, a[i].x) - b;
        h[idx[i]] = a[i].h;
    }
    build(1, 1, n);
    for(int i = 1;i <= n; i++) {
        int pos = idx[i];
        /*------查找左边1-pos--------*/
        int lx = query_max(1, 1, pos);
        if(lx > h[pos]) {
            int l = 1, r = pos, ans = 0;
            while(l <= r) {
                int m = (l + r) / 2;
                lx = query_max(1, m, pos);
                if(lx > h[pos]) ans = m, l = m + 1;
                else r = m - 1;
            }
            h[ans] = h[pos]; modify(1, ans, h[pos]);
        }
        if(pos == n) continue;
        /*---------查找右边pos+1-n-----------*/
        int rx = query_max(1, pos + 1, n);
        if(rx <= h[pos]) {
            rx = query_min(1, pos + 1, n);
            int l = pos + 1, r = n, ans = 0;
            while(l <= r) {
                int m = (l + r) / 2;
                int nrx = query_min(1, pos + 1, m);
                if(nrx == rx) ans = m, r = m - 1;
                else l = m + 1;
            }
            h[ans] = h[pos]; modify(1, ans, h[pos]);
        }
    }
    for(int i = 1;i <= n; i++) cout << h[idx[i]] << " ";
    cout << endl;
}

signed main() {
    solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值