#include <iostream>
#include <sstream>
#include <vector>
#include <iomanip>
#include <algorithm>
#include <set>

struct TestData
{
  int testNum;
  std::string input;
  std::string expectedOutput;
};

struct TestResult
{
  std::string testOutput;
  bool testPassed;
};

struct Job
{
  int daysUntilPayment;
  int rewardUponCompletion;

  std::string toString() const
  {
    std::ostringstream oss;
    oss << "{ days: " << daysUntilPayment << ", reward: " <<  rewardUponCompletion << " }";
    return oss.str();
  }

  bool operator<(const Job& rhs) const
  {
    return
      this->daysUntilPayment < rhs.daysUntilPayment || 
      (
        !(rhs.daysUntilPayment < this->daysUntilPayment)
        && this->rewardUponCompletion < rhs.rewardUponCompletion
      );
  }
};

void testSolution();
TestResult runTestCase(const TestData& testData);
std::string formatResult(const TestData& testData, const TestResult& testResult);
void solution(std::istream& is, std::ostream& os);
long long int findMaximumReward(const std::vector<Job>& jobsAvailable, int maxDays);

static const bool IS_TESTING = false;

// define test data
static const std::vector<TestData> testDatas = {
  {1, "3 4\n4 3\n4 1\n2 2\n", "5\n"},
  {2, "5 3\n1 2\n1 3\n1 4\n2 1\n2 3\n", "10\n"},
  {3, "1 1\n2 1\n", "0\n"},
};

int main()
{
  if (IS_TESTING)
    testSolution();
  else
    solution(std::cin, std::cout);

  return 0;
}

void testSolution()
{
  for (const auto& testData : testDatas) {
    auto testResult = runTestCase(testData);

    std::cout << formatResult(testData, testResult);
  }
}

TestResult runTestCase(const TestData& testData)
{
    std::istringstream iss {testData.input};
    std::ostringstream oss {};

    solution(iss, oss);

    auto testOutput = oss.str();
    auto testPassed = testData.expectedOutput.compare(testOutput) == 0;
    
    return {testOutput, testPassed};
}

std::string formatResult(const TestData& testData, const TestResult& testResult)
{
  std::ostringstream oss {};

  oss << "Test " << std::setw(2) << testData.testNum << ": "
    << (testResult.testPassed ? "Passed!" : "Failed!") << std::endl;
  
  oss << "\tExpected: " << testData.expectedOutput << "\tActual  : " << testResult.testOutput << std::endl;

  return oss.str();
}

// solution to the problem
void solution(std::istream& is, std::ostream& os)
{
  // read input
  int numJobs, maxDays; is >> numJobs >> maxDays;
  auto jobs = std::vector<Job>(numJobs);
  
  for (auto jobNum = 0; jobNum < numJobs; ++jobNum) {
    std::string daysUntilPayment, rewardUponCompletion;
    is >> daysUntilPayment >> rewardUponCompletion;
    jobs[jobNum] = {
      std::stoi(daysUntilPayment),
      std::stoi(rewardUponCompletion)
    };
  }
  std::sort(jobs.begin(), jobs.end());

  // core algorithm
  os << findMaximumReward(jobs, maxDays) << std::endl;
}

long long int findMaximumReward(const std::vector<Job>& jobsSorted, int maxDays)
{
  std::multiset<int> sortedRewards; // ascending order
  int jobNum = 0;
  long long int maxReward = 0;

  for (int d = 1; d <= maxDays; d++) {
    // add job to reward, where daysUntilPayment == d
    while (jobNum < jobsSorted.size() && jobsSorted[jobNum].daysUntilPayment == d)
      sortedRewards.insert(jobsSorted[jobNum++].rewardUponCompletion);

    // take the highest reward seen so far
    if (sortedRewards.size() > 0) {
        auto highestReward = --sortedRewards.end();
        maxReward += *highestReward;
        sortedRewards.erase(highestReward);
    }
  }

  return maxReward;
}


Measured memory_bytes: 5632