1. 程式人生 > 其它 >程式碼源每日一題Div2 106 訂單編號 題解

程式碼源每日一題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;
}