有N头牛在畜栏中吃草。

每个畜栏在同一时间段只能提供给一头牛吃草,所以可能会需要多个畜栏。

给定N头牛和每头牛开始吃草的时间A以及结束吃草的时间B,每头牛在[A,B]这一时间段内都会一直吃草。

当两头牛的吃草区间存在交集时(包括端点),这两头牛不能被安排在同一个畜栏吃草。

求需要的最小畜栏数目和每头牛对应的畜栏方案。
输入格式

第1行:输入一个整数N。

第2…N+1行:第i+1行输入第i头牛的开始吃草时间A以及结束吃草时间B,数之间用空格隔开。
输出格式

第1行:输入一个整数,代表所需最小畜栏数。

第2…N+1行:第i+1行输入第i头牛被安排到的畜栏编号,编号是从1开始的 连续 整数,只要方案合法即可。
数据范围

1≤N≤50000
,
1≤A,B≤1000000

输入样例:

5
1 10
2 4
3 6
5 8
4 7

输出样例:

4
1
2
3
2
4

推荐y总讲题视频 : https://www.acwing.com/video/88/

就是给定很多条线段(可能有重叠), 把他们分组,每组要求组内无重叠线段,
问最小多少组 ?


  线段分组 : 有很多条可能重叠的线段,求最小分组,每组里面没有重叠线段

  第一组 :    --------    --------   ---      (组内不重叠)
  第二组 :      ---  ------    ----------     (组内不重叠)
  第三组 :       ------------------ ----      (组内不重叠)

考虑贪心 :

  • 先把所有线段按起点递增排序,起点相同按终点递增排序
  • 然后扫描每个线段,把第一个线段入堆(因为至少要一个组),
  • 剩下的线段如果和堆里的线段都交集,就必须新建一个组
  • 同时更新这组(堆顶)的末端点

假设按照以上做法求出来的最小值是m

假设最优解是n, 则n<m

观察到到达m是从1开始每次+1

在枚举的时候,一定存在某个时刻出现n变成n+1的过程

当某一个区间的起点大于其他的某个终点时, 才要新建一个畜栏

1. -------------
2.                -------
3.         -------   (3号需要新建畜栏)
4.                        ---------

代码如下

#define debug
#ifdef debug
#include <time.h>
#include "/home/majiao/mb.h"
#endif

#include <iostream>
#include <algorithm>
#include <vector>
#include <string.h>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <math.h>

#define MAXN ((int)1e5+7)
#define ll long long int
#define INF (0x7f7f7f7f)
#define fori(lef, rig) for(int i=lef; i<=rig; i++)
#define forj(lef, rig) for(int j=lef; j<=rig; j++)
#define fork(lef, rig) for(int k=lef; k<=rig; k++)
#define QAQ (0)

using namespace std;

#define show(x...) \ do { \ cout << "\033[31;1m " << #x << " -> "; \ err(x); \ } while (0)

void err() { cout << "\033[39;0m" << endl; }
template<typename T, typename... A>
void err(T a, A... x) { cout << a << ' '; err(x...); }

namespace FastIO{

	char print_f[105];
	void read() {}
	void print() { putchar('\n'); }

	template <typename T, typename... T2>
		inline void read(T &x, T2 &... oth) {
			x = 0;
			char ch = getchar();
			ll f = 1;
			while (!isdigit(ch)) {
				if (ch == '-') f *= -1; 
				ch = getchar();
			}
			while (isdigit(ch)) {
				x = x * 10 + ch - 48;
				ch = getchar();
			}
			x *= f;
			read(oth...);
		}
	template <typename T, typename... T2>
		inline void print(T x, T2... oth) {
			ll p3=-1;
			if(x<0) putchar('-'), x=-x;
			do{
				print_f[++p3] = x%10 + 48;
			} while(x/=10);
			while(p3>=0) putchar(print_f[p3--]);
			putchar(' ');
			print(oth...);
		}
} // namespace FastIO
using FastIO::print;
using FastIO::read;

int n, m, Q, K;
/* 线段分组 : 有很多条可能重叠的线段,求最小分组,每组里面没有 第一组 : -------- -------- --- (组内不重叠) 第二组 : --- ------ ---------- (组内不重叠) 第三组 : ------------------ ---- (组内不重叠) */

struct Node {
	int lef, rig, id; //要记录id
} a[MAXN];

bool cmplef(Node& A, Node& B) { //开始先按左端点sort
	return A.lef == B.lef ? A.rig < B.rig : A.lef < B.lef;
}

struct cmpque { //堆里结束点最小的在堆顶
	bool operator () (const Node& A, const Node& B) const {
		return A.rig > B.rig;
	}
} ;

#define TOP (q.top())
#define TRIG (q.top().rig)

int main() {
#ifdef debug
	freopen("test", "r", stdin);
	clock_t stime = clock();
#endif
	read(n);
	for(int i=1; i<=n; i++) {
		read(a[i].lef, a[i].rig);
		a[i].id = i;             //别忘了记录id
	}

	sort(a+1, a+1+n, cmplef);    //先按开始时间排序

	//小根堆,堆顶是最早结束的牛
	priority_queue<Node, vector<Node>, cmpque> q;

	int ans[MAXN] = { 0 }, timer = 0/*畜栏id递增*/, cnt = 0/*记录需要几个畜栏*/;

	/* 这里贪图方便,我把每个入堆以后的node.lef设置成畜栏的id, 因为入堆后lef不影响,或者说未被使用,反正空着也是空着, 就直接用来存畜栏的id了 用timer表示畜栏id */
	for(int i=1; i<=n; i++) {
		if(q.empty() || TRIG>=a[i].lef) { //当前没有畜栏空着,就新建一个 
			cnt ++;
			a[i].lef = ++ timer;    //把堆内的node.lef用来保存畜栏id
			q.push(a[i]);
			ans[a[i].id] = timer;   //记录答案 
		} else {
			Node no = TOP;          //堆顶的牛吃完了,这个畜栏就空了
			q.pop();
			no.rig = a[i].rig;      //第i头牛进去吃
			q.push(no);
			ans[a[i].id] = no.lef;
		}
	}
	printf("%d\n", cnt);
	for(int i=1; i<=n; i++)
		printf("%d\n", ans[i]);


#ifdef debug
	clock_t etime = clock();
	printf("rum time: %lf 秒\n",(double) (etime-stime)/CLOCKS_PER_SEC);
#endif 
	return 0;
}