#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll NEG = (ll)-4e18;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, T;
if (!(cin >> n >> T)) return 0;
vector<int> t(n), q(n);
for (int i = 0; i < n; ++i) cin >> t[i] >> q[i];
// group tasks by cap = T - t[i]
vector<vector<int>> byCap(T+1);
for (int i = 0; i < n; ++i) {
int cap = T - t[i];
if (cap >= 0 && cap <= T) byCap[cap].push_back(q[i]);
}
// sort each level decreasing and build prefix sums
vector<vector<ll>> pref(T+1);
for (int h = 0; h <= T; ++h) {
auto &v = byCap[h];
sort(v.begin(), v.end(), greater<int>());
pref[h].assign(v.size()+1, 0);
for (size_t k = 1; k <= v.size(); ++k) pref[h][k] = pref[h][k-1] + v[k-1];
}
int MAXC = n; // số nút ở 1 mức không vượt quá n
// D[h][c] : giá trị tối đa đã thu được từ mức > h khi hiện có c nút ở mức h
vector<vector<ll>> D(T+1, vector<ll>(MAXC+1, NEG));
// Khởi: ở mức sâu nhất (h = T) "không có cấp sâu hơn", nên đóng góp dưới cùng = 0
// Ta cho D[T][c] = 0 cho mọi c (tức chưa thu gì từ dưới)
for (int c = 0; c <= MAXC; ++c) D[T][c] = 0;
// sweep từ h = T xuống h = 0
ll answer = 0; // có thể là 0 (không chọn task nào)
for (int h = T; h >= 0; --h) {
// nếu h == 0 thì khi cập nhật lên h-1 thực tế là kết thúc -> ta thu kết quả vào answer
for (int c = 0; c <= MAXC; ++c) {
if (D[h][c] == NEG) continue;
int maxk = min((int)byCap[h].size(), c);
for (int k = 0; k <= maxk; ++k) {
ll val = D[h][c] + pref[h][k];
int newC = (c + k + 1) / 2; // ceil((c+k)/2)
if (h == 0) {
// ở mức 0: muốn có đúng 1 nút (root). newC phải là 1
if (newC == 1) answer = max(answer, val);
} else {
// cập nhật cho mức trên (h-1)
D[h-1][newC] = max(D[h-1][newC], val);
}
}
}
}
cout << answer << '\n';
return 0;
}