2018 Multi-University Training Contest 1 1004. Distinct Values

版权声明:本文为博主原创文章,转载清注明出处 https://blog.csdn.net/Jasmineaha/article/details/81671673

Problem Description

Chiaki has an array of n positive integers. You are told some facts about the array: for every two elements ai and aj in the subarray al..r (l≤i<j≤r), ai≠ajholds.
Chiaki would like to find a lexicographically minimal array which meets the facts.

Input

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
The first line contains two integers n and m (1≤n,m≤105) -- the length of the array and the number of facts. Each of the next m lines contains two integers li and ri (1≤li≤ri≤n).
It is guaranteed that neither the sum of all n nor the sum of all m exceeds 106.

Output

For each test case, output n integers denoting the lexicographically minimal array. Integers should be separated by a single space, and no extra spaces are allowed at the end of lines.

Sample Input

3 2 1 1 2 4 2 1 2 3 4 5 2 1 3 2 4

Sample Output

1 2 1 2 1 2 1 2 3 1 1

 

Description: 

对于一个长度为 N 的序列,给定 M 个区间,要求对每个区间 [Li, Ri],都有 ai ≠ aj(l <= i < j <= r)。构造一个符合条件的字典序最小的序列

Solution:

因为要保证字典序最小,所以每次对于每个位置,都要优先考虑放置较小的数,而保证每个区间内不能有重复的数即涉及在某一个区间内相同的数字只能使用一次。

首先对区间的左端点进行从小到大的规则排序,如果左端点相同,则右端点按照从大到小的规则进行排序。

然后:

1. 对于当前位置,如果没有被任何区间覆盖过,则填1;

2. 如果当前区间A完全包含在另外一个区间B(也就是当前区间的前一个区间)内,那么区间A不需要被考虑;

3. 如果区间之间有重叠的部分:假设区间A左端点在前,区间B左端点在后,那么重叠部分的数字就不能够使用,而A中未重叠部分的数字可以被重新填入B的未重叠部分。这样就产生了一个释放被使用数字,和使用数字的概念。

对于一个区间,采用 L 指针和 R 指针表示区间的两边,当一个区间构造完成后,先移动 L 指针,使得左边不重叠部分的数先被释放,将这些被释放的数投入到优先队列中,保证下次使用这些数字时保证用到的顺序都是最小的;然后移动 R 指针,直到 R 指针移动到下一个区间的右边界。在移动的过程中,可以直接利用之前释放的数构造下一个区间的不重叠部分(也就是将数字从优先队列中弹出)。

那么回存在一个问题:当前优先队列中存放的数不够多。如果我们选择一开始就将 N 个可能被使用的数字全都投入到优先队列中候选,很明显复杂度太高。那么可以记录一个当前使用的最大值 maxx,如果当前优先队列中存放的数不够用时,就令当前位置填 ++maxx。

Code:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#include <stack>
#define mst(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const LL Mod = 998244353;
const double eps = 1e-9;
const int MaxN = 1e5 + 5;

struct node{
	int l, r;
	bool operator <(node b) const {
		return l < b.l || (l == b.l && r > b.r);
	}
}a[MaxN];

priority_queue<int, vector<int>, greater<int> >q;
int ans[MaxN];

int main()
{
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);

	int t; scanf("%d", &t);
	while(t--) {
		int n, m; 
		scanf("%d %d", &n, &m);
		mst(ans, 0);
		while(!q.empty()) q.pop();
		for(int i = 1; i <= m; i++) scanf("%d %d", &a[i].l, &a[i].r);
		sort(a + 1, a + 1 + m);

		//假设对于两个形成交叉的区间[l1, r1], [l2, r2](l2 > r1, r2 > r1)
		//那么考虑将这两个区间分成三段,A[l1, l2], B[l2, r1], C[r1, r2];
		int maxx = 0, l = a[1].l, r = a[1].r;
		for(int i = l; i <= r; i++) ans[i] = ++maxx;
		for(int i = 2; i <= m; i++) {
			while(l < a[i].l) {  
				if(l <= r) q.push(ans[l]); //对于A段区间,也就是可释放数字,加入优先队列
				l++;
			}
			while(r < a[i].r) { //对于C段区间,也就是使用之前释放过的数字
				r++;
				if(!q.empty()) { 
					if(r >= l) ans[r] = q.top(), q.pop();
				}
				else ans[r] = ++maxx; //如果队列为空,也就是优先队列中的数字不够使用的情况
			}
		}
		for(int i = 1; i <= n; i++) 
			printf("%d%c", ans[i] == 0 ? 1 : ans[i], i == n ? '\n' : ' '); //ans[i] = 0说明这个位置没有被任意一个区间覆盖过,填1
	}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Jasmineaha/article/details/81671673