1. 程式人生 > >2018 Multi-University Training Contest 1 1004. Distinct Values

2018 Multi-University Training Contest 1 1004. Distinct Values

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;
}