// Original C++ solution (slower):
// https://atcoder.jp/contests/dp/tasks/dp_e



#include <algorithm>

#include <cmath>

#include <iomanip>

#include <iostream>

#include <map>

#include <numeric>

#include <set>

#include <string>

#include <tuple>

#include <vector>

using namespace std;



typedef long long ll;

typedef pair<int, int> PII;

typedef pair<ll, ll> PLL;

#define INF (1e9)

#define MOD (1e9 + 7)

#define LINF (1e18)

#define FOR(i, a, b) for (int i = (a); i < (b); ++i)

#define REP(i, n) for (int i = 0; i < (n); ++i)

#define REPR(i, n) for (int i = n; i >= 0; i--)

#define FOREACH(x, a) for (auto &(x) : (a))

#define ALL(obj) (obj).begin(), (obj).end()

#define ALLR(obj) (obj).rbegin(), (obj).rend()

// int gcd(long a, long b) { return b ? gcd(b, a % b) : a; }

// int lcm(long a, long b) { return a * b / gcd(a, b); }



int main() {

    ios::sync_with_stdio(false);

    cin.tie(0);

    int n, w, i1, i2;

    ll t, ans = 0;

    cin >> n >> w;

    // dp は重さに対する価値を保存する

    // vector<int> dp(100001, INF);

    map<int, ll> dp, cp;

    dp[0] = 0;

    REP(i, n) {

        cin >> i1 >> i2;

        // 追加用のdpを分ける

        cp = dp;

        for (auto x : dp) {

            ll weight = x.first + i1;

            if (weight <= w) {

                if (cp.count(weight) == 0) {

                    t = x.second + i2;

                } else {

                    t = max(cp[weight], x.second + i2);

                }

                cp[weight] = t;

                ans = max(ans, t);

            }

        }

        dp = cp;

    }

    cout << ans << endl;



    return 0;

}

// Optimized solution to the same problem (faster):
#include <algorithm>

#include <cstdio>

#include <vector>



typedef long long int ll;



struct state {

    ll aw;

    ll av;

    state(ll aw = 0, ll av = 0) : aw(aw), av(av) {}

};



struct item {

    int w;

    int v;

    double d;

    item(int w, int v) : w(w), v(v), d((double)v / w) {}

};



int n, w;



ll lower;



std::vector<item> is;

std::vector<state> dp[2];



void add(const std::vector<state> &s, const item &k, std::vector<state> &u,

         ll uw) {

    auto i = s.cbegin();

    auto j = s.cbegin();

    while (i != s.cend() && j != s.cend()) {

        if (i->aw > uw) {

            i = s.cend();

            break;

        }

        ll jav = j->av + k.v;

        ll jaw = j->aw + k.w;

        if (jaw > uw) {

            j = s.cend();

            break;

        }

        if (i->aw <= jaw && i->av >= jav) {

            ++j;

        } else if (i->aw >= jaw && i->av <= jav) {

            ++i;

        } else if (i->aw < jaw) {

            u.push_back(*i++);

        } else {

            u.emplace_back(state(jaw, jav));

            ++j;

        }

    }

    while (i != s.cend()) {

        if (i->aw > uw)

            return;

        u.push_back(*i++);

    }

    while (j != s.cend()) {

        ll jav = j->av + k.v;

        ll jaw = j->aw + k.w;

        if (jaw > uw)

            return;

        u.emplace_back(state(jaw, jav));

        ++j;

    }

}



void remove(const std::vector<state> &s, const item &k, std::vector<state> &u,

            ll uw) {

    auto i = s.cbegin();

    auto j = s.cbegin();

    while (i != s.cend() && j != s.cend()) {

        if (i->aw > uw) {

            i = s.cend();

            break;

        }

        ll jav = j->av - k.v;

        ll jaw = j->aw - k.w;

        if (jaw > uw) {

            j = s.cend();

            break;

        }

        if (i->aw <= jaw && i->av >= jav) {

            ++j;

        } else if (i->aw >= jaw && i->av <= jav) {

            ++i;

        } else if (i->aw < jaw) {

            u.push_back(*i++);

        } else {

            u.emplace_back(state(jaw, jav));

            ++j;

        }

    }

    while (i != s.cend()) {

        if (i->aw > uw)

            return;

        u.push_back(*i++);

    }

    while (j != s.cend()) {

        ll jav = j->av - k.v;

        ll jaw = j->aw - k.w;

        if (jaw > uw)

            return;

        u.emplace_back(state(jaw, jav));

        ++j;

    }

}



ll upper;



inline void shrink(const std::vector<state> &s, double d0, double d1,

                   std::vector<state> &u) {

    for (auto x : s) {

        ll t = (x.aw > w) ? x.av - (x.aw - w) * d0 : x.av + (w - x.aw) * d1;

        if (t >= lower)

            u.push_back(x);

        if (t > upper)

            upper = t;

    }

}



int main() {

    scanf("%d%d", &n, &w);

    is.reserve(200);

    dp[0].reserve(300);

    dp[1].reserve(300);



    is.emplace_back(item(1, 0));



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

        int ww, vv;

        scanf("%d%d", &ww, &vv);

        if (ww <= w)

            is.emplace_back(item(ww, vv));

    }

    n = is.size() - 1;



    std::sort(is.begin() + 1, is.end(),

              [](const item &x, const item &y) { return x.d > y.d; });

    is.emplace_back(item(1, 0));



    ll aw = 0;

    ll av = 0;

    int ii;

    for (ii = 1; ii <= n; ii++) {

        const item &x = is[ii];

        if (aw + x.w > w)

            break;

        aw += x.w;

        av += x.v;

    }



    dp[0].emplace_back(state(aw, av));



    lower = av;

    ll sumw = w + aw;



    for (int i = ii - 1, j = ii; i > 0 || j <= n;) {

        if (i > 0) {

            sumw -= is[i].w;

            remove(dp[0], is[i], dp[1], sumw);

            dp[0].resize(0);

            lower = (std::upper_bound(

                         dp[1].cbegin(), dp[1].cend(), w,

                         [](int w, const state &x) { return x.aw > w; }) -

                     1)

                        ->av;

            shrink(dp[1], is[i - 1].d, is[j].d, dp[0]);

            dp[1].resize(0);

            --i;

        }



        if (j <= n) {

            add(dp[0], is[j], dp[1], sumw);

            dp[0].resize(0);

            lower = (std::upper_bound(

                         dp[1].cbegin(), dp[1].cend(), w,

                         [](int w, const state &x) { return x.aw > w; }) -

                     1)

                        ->av;

            shrink(dp[1], is[i].d, is[j + 1].d, dp[0]);

            dp[1].resize(0);

            ++j;

        }

        if (lower == upper)

            break;

    }



    lower = (std::upper_bound(dp[0].cbegin(), dp[0].cend(), w,

                              [](int w, const state &x) { return x.aw > w; }) -

             1)

                ->av;

    printf("%lld\n", lower);



    return 0;

}

// Fastest known solution for this problem: