Problem:
Score : 400 points

Problem Statement

There are N one-off jobs available. If you take the i-th job and complete it, you will earn the reward of B_i after A_i days from the day you do it.

You can take and complete at most one of these jobs in a day.

However, you cannot retake a job that you have already done.

Find the maximum total reward that you can earn no later than M days from today.

You can already start working today.

Constraints

All values in input are integers.

1 \leq N \leq 10^5

1 \leq M \leq 10^5

1 \leq A_i \leq 10^5

1 \leq B_i \leq 10^4

Input

Input is given from Standard Input in the following format:

N M
A_1 B_1
A_2 B_2
\vdots
A_N B_N

Output

Print the maximum total reward that you can earn no later than M days from today.

Sample Input 1

3 4
4 3
4 1
2 2

Sample Output 1

5

You can earn the total reward of 5 by taking the jobs as follows:

Take and complete the first job today. You will earn the reward of 3 after four days from today.

Take and complete the third job tomorrow. You will earn the reward of 2 after two days from tomorrow, that is, after three days from today.

Sample Input 2

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

Sample Output 2

10

Sample Input 3

1 1
2 1

Sample Output 3

0

Solution in C# (Mono 4.6.2.0) (judge verdict: Time Limit Exceeded)

using System;
using System.Text;
using System.Linq;
using System.Collections;
using System.Collections.Generic;


class Program {

	static long mod = 1000000007;
	static int[] ans;
	static void Main(string[] args) {

		char[] cs = new char[] { ' ' };
		//char[] cs = new char[] { ' ', ',' };

		//int n = int.Parse(Console.ReadLine());
		//int val = int.Parse(Console.ReadLine());
		//int[] vals = parseAry(Console.ReadLine().Split(cs));
		//string str = Console.ReadLine();
		//string[] strs = Console.ReadLine().Split(cs);

		string res = "";

		int[] vals = parseAry(Console.ReadLine().Split(cs));
		int n = vals[0];
		int m = vals[1];
		//Dictionary<int, List<int>> dic = new Dictionary<int, List<int>>();
		List<List<int>> mot = new List<List<int>>();

		PriorityQueue pq = new PriorityQueue();

		for ( int i = 0; i <= m; i++ ) {
 			mot.Add(new List<int>());
		}

		long ans = 0;
		for ( int i = 0; i < n; i++ ) {
			vals = parseAry(Console.ReadLine().Split(cs));
			//if ( !dic.ContainsKey(vals[0]) ) { dic.Add(vals[0], new List<int>()); }

			if ( vals[0] > m ) { continue; }
			mot[vals[0]].Add(vals[1]);

		}

		for ( int i = 0; i <=m; i++ ) {

			if ( mot[i].Count==0 && pq.Count==0 ) { continue; }
			List<int> li=mot[i];
				for ( int p = 0; p < li.Count; p++ ) {
					pq.Enqueue(new PriQ() { rank = li[p] });
				pq.RemoveOverRangeFirst(m - i + 1);
				}
				if ( pq.Count > 0 ) {
					ans += pq.DequeueLast().rank;
				}


		}


			Console.WriteLine(ans);

			return;

	}


	static int xs(List<int> li) {
		int index = -1;
		int max = -1;
		for ( int i = 0; i < li.Count; i++ ) {
			if ( max < li[i] ) { max = li[i]; index = i; }
		}

		return index;
	}
 

	//	int配列をスペース区切りの文字列に変換
	static string stringBuilderArrayInt(int[] a) {
		StringBuilder sb = new StringBuilder();

		for ( int i = 0; i < a.Length; i++ ) {
			if ( a[i] == 1 ) { sb.Append(i + " "); }
		}
		if ( sb.Length > 1 ) {
			sb = sb.Remove(sb.Length - 1, 1);
		}
		return sb.ToString(); ;
	}


	//	文字列strをindex番目からdで上書き
	static string strOverWrite(string str, int i, string s) {
		return str.Remove(i, s.Length).Insert(i, s);
	}

	//　String[]  ----->  int[]
	static int[] parseAry(string[] str) {
		int[] nums = new int[str.Length];
		for ( int i = 0; i < str.Length; i++ ) { nums[i] = int.Parse(str[i]); }
		return nums;
	}
	//　String[]  ----->  long[]
	static long[] parseAryL(string[] str) {
		long[] nums = new long[str.Length];
		for ( int i = 0; i < str.Length; i++ ) { nums[i] = long.Parse(str[i]); }
		return nums;
	}

	//文字列を1文字ずつString[]に入れる
	static string[] strAry(String str) {
		string[] re = new string[str.Length];
		for ( int i = 0; i < str.Length; i++ ) { re[i] = str[i].ToString(); }
		return re;
	}



	public class PriQ {
		public int rank { get; set; }
		public string rankstr { get; set; }
		public int num { get; set; }
		public int[] nums { get; set; }
		public Object obj { get; set; }
		public PriQ() { }
	}

	public class PriorityQueue {
		List<PriQ> list = new List<PriQ>();
		public PriorityQueue() { }
		public PriorityQueue(PriQ p) { list.Add(p); }
		public void Enqueue(PriQ item) { list.Insert(Binary_Search(item), item); }
		public void EnqueueStr(PriQ item) { list.Insert(Binary_Search_Str(item), item); }
		public PriQ Dequeue() { PriQ r = list[0]; list.RemoveAt(0); return r; }
		public PriQ DequeueLast() { PriQ r = list[list.Count - 1]; list.RemoveAt(list.Count - 1); return r; }
		public PriQ Peek() { return list[0]; }
		public PriQ Get(int n) { return list[n]; }
		public PriQ Last() { return list[list.Count - 1]; }
		public void Remove(int n) { list.RemoveAt(n); }
		public void RemoveRangeEnd(int n) {
			if ( n > list.Count ) { return; }
			list.RemoveRange(n, list.Count - n);
		}
		public void RemoveOverRangeFirst(int n) {
			if ( n > list.Count ) { return; }
			list.RemoveRange(0, list.Count-n);
		}
		public void Clear() { list.Clear(); }
		public int Count { get { return list.Count; } }

		private int Binary_Search(PriQ pq) {
			int Lower = 0;
			int Upper = list.Count - 1;
			while ( Lower <= Upper ) {
				int Center = ( Lower + Upper ) >> 1;
				if ( pq.rank > list[Center].rank ) {
					Lower = Center + 1;
				} else if ( pq.rank < list[Center].rank ) {
					Upper = Center - 1;
				} else {
					return Center;
				}
			}
			return Lower;
		}
		private int Binary_Search_Str(PriQ pq) {
			int Lower = 0;
			int Upper = list.Count - 1;
			while ( Lower <= Upper ) {
				int Center = ( Lower + Upper ) >> 1;
				if ( string.CompareOrdinal(pq.rankstr, list[Center].rankstr) > 0 ) {
					Lower = Center + 1;
				} else if ( string.CompareOrdinal(pq.rankstr, list[Center].rankstr) < 0 ) {
					Upper = Center - 1;
				} else {
					return Center;
				}
			}
			return Lower;
		}
	}
}