代码源每日一题Div2 106 订单编号 题解

题目链接

简要题意

给定 \(n\) 个订单,第 \(i\) 个订单的编号为 \(a_i\)

现在我们需要对订单进行重新编号,从前到后依次进行,要求对于任意一个订单,必须要选择一个大于等于旧编号,并没被用过(是指在新编号中没被用过,也就是说这些新编号要互不相同)的最小正整数作为新编号。

模拟完后,依次输出这些订单的新编号。

\(n\leq 5*10^5,1\leq a_i\leq 10^9\)

题解

本题就是 set 维护区间的经典应用,如下:

  1. 设立一个存放区间的 set,按照右端点大小来排序
  2. 按照顺序重新定下编号,对于原来编号 \(x\),找区间 \([L,R]\)\(R\) 应当在大于等于 \(x\) 的情况下尽可能小
  3. 给这个订单定一个新编号 \(\max(L,x)\),并将这个区间进行拆分,随后再 insert 进去

总复杂度约为 \(O(\sum\limits_{i=1}^n(1+\log i))=O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
set<pair<int, int> > s;
void insert(int L, int R) {
    if (L > R) return;
    s.insert(make_pair(R, L));
}
int main()
{
    insert(1, 2e9);
    int n;
    scanf("%d", &n);
    for (int i = 1, x; i <= n; ++i) {
        scanf("%d", &x);
        auto it = s.lower_bound(make_pair(x, 0));
        int res = max(x, it->second);
        insert(it->second, res - 1);
        insert(res + 1, it->first);
        printf("%d ", res);
        s.erase(it);
    }
    return 0;
}
posted @ 2022-05-11 18:09  cyhforlight  阅读(141)  评论(0)    收藏  举报