题意:让你在保证m组闭区间内的数字都不同的前提下,生成一个长度为n,字典序最小的数列。
字典序最小,就是让开头的数字尽可能的小,所以我们要尽可能的填1,填不了1再填2、填3、4….
所以,没有区间束缚的位置,我们全填上1。
有区间束缚的位置,我们也要尽可能的填1,但是他要求区间内没有重复,这就要求我们在对一个区间进行填充的时候,从小到大维护这个区间内能填充的值。
当然是优先队列啦。
优先队列在这里能做到:保证区间内值不重复(出队)、保证字典序最小(始终保持顶端有序)、将区间结束后释放出来的数字及时使用。
所以,用结构体存储区间并按左边界排序,再用优先队列维护能填的数字就可以了!
更多的细节看代码的注释吧。
ac代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
struct Seg {
int l, r;
bool operator < (const Seg &a) const {
return l < a.l;
}
} Segs[maxn];
int main() {
int t, n, m, num[maxn];
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++) {
scanf("%d%d", &Segs[i].l, &Segs[i].r);
}
sort(Segs, Segs + m);
priority_queue<int, vector<int>, greater<int> > wait;
for(int i = 1; i <= n; i++) {
wait.push(i);//将可以填的数字全部入队
}
memset(num, 0, sizeof(num));
int pre = -1;//前一个区间的索引
for(int i = 0; i < m; i++) {
Seg get = Segs[i];
//试想,如果一个区间被另一个区间所涵盖,就不用考虑这个区间了对吧?
//由于已经按左边界排序,所以只考虑右边界,跳过包含区间。
if(num[get.r] > 0) {
continue;
}
//释放操作,及时释放被前一个区间占用的数字(入队)
//如果两个区间有重合,则只释放到现在区间的左边界 - 1
if(pre != -1) {
for(int j = Segs[pre].l; j <= Segs[pre].r && j < Segs[i].l; j++) {
wait.push(num[j]);
}
}
// 考虑区间重合的情况,从没填的部分开始
int j = get.l;
if(pre != -1) {
j = max(j, Segs[pre].r + 1);
}
for(; j <= get.r; j++) {
num[j] = wait.top();
wait.pop();
}
pre = i;
}
for(int i = 1; i <= n; i++) {
// 没有被区间束缚的部分,在输出时就能顺手填1
if(num[i] == 0) {
num[i] = 1;
}
printf("%d", num[i]);
if(i < n) {
printf(" ");
}
}
printf("\n");
}
return 0;
}