// Original C++ solution (slower):
#include <bits/stdc++.h>

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

using namespace std;

typedef long long LL;

 

const int dx[4] = {1, 0, -1, 0};

const int dy[4] = {0, 1, 0, -1};

inline bool valid(int x, int w) { return 0 <= x && x < w; }

 

void iostream_init() {

    ios::sync_with_stdio(false);

    cin.tie(0);

    cout.setf(ios::fixed);

    cout.precision(12);

}

 

class Knapsack {

    typedef long long LL;

    struct item {

        LL v, w;

    };

 

    LL W;

    vector<item> items;

    LL lv;

 

    LL solve_rec(size_t k, LL v, LL w) {

        if (w + items[k].w > W) return solve_rec(k+1, v, w);

        LL cv = v, cw = w;

        for (size_t i = k; i < items.size(); ++i) {

            if (cw + items[i].w <= W) {

                cw += items[i].w;

                cv += items[i].v;

            }

        }

        if (lv < cv) lv = cv;

        double fv = v, fw = w;

        for (size_t i = k; i < items.size(); ++i) {

            if (fw + items[i].w <= W) {

                fw += items[i].w;

                fv += items[i].v;

            } else {

                fv += items[i].v * (W - fw) / items[i].w;

                break;

            }

        }

        if (fv - lv < 1 || fv < lv) return lv;

        solve_rec(k+1, v+items[k].v, w+items[k].w);

        return solve_rec(k+1, v, w);

    }

 

    public:

    // 価値v, 重みwのアイテムを追加

    void add_item(LL v, LL w) {

        items.push_back({v, w});

    }

 

    // 重みの合計がW以下になるようにアイテムを選んだときの価値の和の最大値を返す．

    LL solve(LL W_) {

        if(items.empty()) return 0;

        W = W_;

        sort(items.begin(), items.end(), [](const item &a, const item &b) {

                return a.v * b.w > a.w * b.v;

                });

        lv = 0;

        return solve_rec(0, 0, 0);

    };

};

 

int main(){

    iostream_init();

    int N, W;

    cin >> N >> W;

    Knapsack dp;

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

        int w, v;

        cin >> w >> v;

        dp.add_item(v, w);

    }

 

    cout << dp.solve(W) << 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: