代码源每日一题Div2 106 订单编号 题解
简要题意
给定 \(n\) 个订单,第 \(i\) 个订单的编号为 \(a_i\)。
现在我们需要对订单进行重新编号,从前到后依次进行,要求对于任意一个订单,必须要选择一个大于等于旧编号,并没被用过(是指在新编号中没被用过,也就是说这些新编号要互不相同)的最小正整数作为新编号。
模拟完后,依次输出这些订单的新编号。
\(n\leq 5*10^5,1\leq a_i\leq 10^9\)
题解
本题就是 set 维护区间的经典应用,如下:
- 设立一个存放区间的 set,按照右端点大小来排序
- 按照顺序重新定下编号,对于原来编号 \(x\),找区间 \([L,R]\),\(R\) 应当在大于等于 \(x\) 的情况下尽可能小
- 给这个订单定一个新编号 \(\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;
}

浙公网安备 33010602011771号