#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128_t; // prevents overflow when we divide M
const i64 NEG = -(1LL<<62); // -INF
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N; i64 M;
if(!(cin >> N >> M)) return 0;
vector<i64> v(N), m(N);
for (int i = 0; i < N; ++i) cin >> v[i] >> m[i];
/* ---- 1. group by identical mass ----------------------------------- */
vector<i64> masses = m;
sort(masses.begin(), masses.end());
masses.erase(unique(masses.begin(), masses.end()), masses.end());
int K = (int)masses.size();
vector<vector<i64>> grp(K);
unordered_map<i64,int> idx;
for (int i=0;i<K;++i) idx[masses[i]]=i;
for (int i = 0; i < N; ++i)
grp[idx[m[i]]].push_back(v[i]);
for (auto &g: grp) sort(g.rbegin(), g.rend()); // big values first
/* ---- 2. pre-compute ratios r[i] ----------------------------------- */
vector<i64> r(K-1);
for (int i=0;i+1<K;++i) r[i] = masses[i+1] / masses[i];
/* ---- 3. DP --------------------------------------------------------- */
i64 C = M / masses[0]; // capacity in w0-units
vector<i64> dp(C+1, NEG);
dp[0] = 0;
i64 mult = 1; // w_i / w0 (in w0-units)
for (int i = 0; i < K; ++i) {
const auto &g = grp[i];
int cnt = (int)g.size();
if (cnt == 0) { // nothing to do
if (i+1 < K) { mult *= r[i]; // scale for next tier
/* compress: keep every mult'th entry */
vector<i64> nxt(r[i], NEG);
for (i64 w = 0; w <= C; ++w)
if (dp[w] != NEG)
nxt[w % r[i]] = max(nxt[w % r[i]], dp[w]);
dp.swap(nxt);
C = r[i]-1;
}
continue;
}
/* bounded-knapsack with weight = mult, values = g[0..cnt-1] */
i64 W = mult; // current item weight
/* deque optimisation, residue classes modulo W */
for (i64 rem = 0; rem < W && rem <= C; ++rem) {
deque<pair<i64,i64>> dq; // (index, value)
for (i64 t = 0, idxw = rem; idxw <= C; ++t, idxw += W) {
i64 base = dp[idxw];
if (base != NEG) { // push candidate
base -= t < (i64)cnt ? 0 : (i64)g.back()+1; // keep monotone
while (!dq.empty() && dq.back().second <= base)
dq.pop_back();
dq.emplace_back(t, base);
}
while (!dq.empty() && dq.front().first + cnt < t)
dq.pop_front();
if (!dq.empty() && t < (i64)cnt) {
i64 gain = g[t]; // t-th best value
dp[idxw] = max(dp[idxw], dq.front().second + gain);
}
}
}
/* finished weight w_i, prepare for next tier */
if (i + 1 < K) {
mult *= r[i];
vector<i64> nxt(r[i], NEG);
for (i64 w = 0; w <= C; ++w)
if (dp[w] != NEG)
nxt[w % r[i]] = max(nxt[w % r[i]], dp[w]);
dp.swap(nxt);
C = r[i]-1;
}
}
cout << *max_element(dp.begin(), dp.end()) << '\n';
return 0;
}