def solve():

    from bisect import bisect_right

    N, W = list(map(int, input().split()))

    a = [tuple(map(int, input().split())) for _ in [0]*N]



    def bitdp(items):

        n = len(items)

        dp = [(0, 0) for _ in [0]*(2**n)]

        for bitset in range(1, 2**n):

            for item_num, (item_v, item_w) in enumerate(items):

                if bitset & 2**item_num:

                    w, v = dp[bitset-2**item_num]

                    if w+item_w <= W:

                        dp[bitset] = (w+item_w, v+item_v)

                    break



        dp.sort()

        l, maxv, inf = [(0, 0)], 0, float("inf")

        append = l.append

        for item, (w, v), (nextw, _) in zip(dp, dp, dp[1:]+[(inf, inf)]):

            if w < nextw and maxv < v:

                append(item)

                maxv = v

        return l



    dp1 = bitdp(a[:N//2])

    dp2 = bitdp(a[N//2:])



    inf = float("inf")

    result = 0

    for w, v in dp1:

        total = v + dp2[bisect_right(dp2, (W-w, inf))-1][1]

        if result < total:

            result = total



    print(result)





if __name__ == "__main__":

    solve()
import sys

from bisect import bisect_right





def meet_in_the_middle(a: list, limit: int) -> tuple:

    first_v, first_w, second_v, second_w = [], [], [], []

    for items, v_append, w_append in (

            (a[:len(a)//2], first_v.append, first_w.append),

            (a[len(a)//2:], second_v.append, second_w.append)

    ):

        enumerated = [(0, 0)]

        for v, w in items:

            enumerated += [(w+_w, v+_v) for _w, _v in enumerated]



        enumerated.sort()

        enumerated = enumerated[:bisect_right(enumerated, (limit, float("inf")))] + [(limit+1, 0)]

        max_v = -1

        for (cw, cv), (nw, nv) in zip(enumerated, enumerated[1:]):

            if cw < nw and max_v < cv:

                v_append(cv)

                w_append(cw)

                max_v = cv



    return (first_v, first_w), (second_v, second_w)





N, W = list(map(int, input().split()))

a = [list(map(int, l.split())) for l in sys.stdin]



(first_v, first_w), (second_v, second_w) = meet_in_the_middle(a, W)

ans = 0

for v, w in zip(first_v, first_w):

    i = bisect_right(second_w, W-w)-1

    new_v = v + second_v[i]

    if ans < new_v:

        ans = new_v

print(ans)

def solve():
    from bisect import bisect_right

    N, W = list(map(int, input().split()))
    a = [tuple(map(int, input().split())) for _ in [0] * N]

    def bitdp(items):
        n = len(items)
        dp = [(0, 0) for _ in [0] * (2**n)]
        for bitset in range(1, 2**n):
            for item_num, (item_v, item_w) in enumerate(items):
                if bitset & 2**item_num:
                    w, v = dp[bitset - 2**item_num]
                    if w + item_w <= W:
                        dp[bitset] = (w + item_w, v + item_v)
                    break
        dp.sort()
        l, maxv, inf = [(0, 0)], 0, float("inf")
        append = l.append
        for item, (w, v), (nextw, _) in zip(dp, dp, dp[1:] + [(inf, inf)]):
            if w < nextw and maxv < v:
                append(item)
                maxv = v
        return l

    dp1 = bitdp(a[: N // 2])
    dp2 = bitdp(a[N // 2 :])
    inf = float("inf")
    result = 0
    for w, v in dp1:
        total = v + dp2[bisect_right(dp2, (W - w, inf)) - 1][1]
        if result < total:
            result = total
    print(result)


if __name__ == "__main__":
    solve()

import sys
from bisect import bisect_right


def meet_in_the_middle(a: list, limit: int) -> tuple:
    first_v, first_w, second_v, second_w = [], [], [], []
    for items, v_append, w_append in (
        (a[: len(a) // 2], first_v.append, first_w.append),
        (a[len(a) // 2 :], second_v.append, second_w.append),
    ):
        enumerated = [(0, 0)]
        for v, w in items:
            enumerated += [(w + _w, v + _v) for _w, _v in enumerated]
        enumerated.sort()
        enumerated = enumerated[: bisect_right(enumerated, (limit, float("inf")))] + [
            (limit + 1, 0)
        ]
        max_v = -1
        for (cw, cv), (nw, nv) in zip(enumerated, enumerated[1:]):
            if cw < nw and max_v < cv:
                v_append(cv)
                w_append(cw)
                max_v = cv
    return (first_v, first_w), (second_v, second_w)


N, W = list(map(int, input().split()))
a = [list(map(int, l.split())) for l in sys.stdin]
(first_v, first_w), (second_v, second_w) = meet_in_the_middle(a, W)
ans = 0
for v, w in zip(first_v, first_w):
    i = bisect_right(second_w, W - w) - 1
    new_v = v + second_v[i]
    if ans < new_v:
        ans = new_v
print(ans)

-def solve(): -    from bisect import bisect_right - -    N, W = list(map(int, input().split())) -    a = [tuple(map(int, input().split())) for _ in [0] * N] - -    def bitdp(items): -        n = len(items) -        dp = [(0, 0) for _ in [0] * (2**n)] -        for bitset in range(1, 2**n): -            for item_num, (item_v, item_w) in enumerate(items): -                if bitset & 2**item_num: -                    w, v = dp[bitset - 2**item_num] -                    if w + item_w <= W: -                        dp[bitset] = (w + item_w, v + item_v) -                    break -        dp.sort() -        l, maxv, inf = [(0, 0)], 0, float("inf") -        append = l.append -        for item, (w, v), (nextw, _) in zip(dp, dp, dp[1:] + [(inf, inf)]): -            if w < nextw and maxv < v: -                append(item) -                maxv = v -        return l - -    dp1 = bitdp(a[: N // 2]) -    dp2 = bitdp(a[N // 2 :]) -    inf = float("inf") -    result = 0 -    for w, v in dp1: -        total = v + dp2[bisect_right(dp2, (W - w, inf)) - 1][1] -        if result < total: -            result = total -    print(result) +import sys +from bisect import bisect_right -if __name__ == "__main__": -    solve() +def meet_in_the_middle(a: list, limit: int) -> tuple: +    first_v, first_w, second_v, second_w = [], [], [], [] +    for items, v_append, w_append in ( +        (a[: len(a) // 2], first_v.append, first_w.append), +        (a[len(a) // 2 :], second_v.append, second_w.append), +    ): +        enumerated = [(0, 0)] +        for v, w in items: +            enumerated += [(w + _w, v + _v) for _w, _v in enumerated] +        enumerated.sort() +        enumerated = enumerated[: bisect_right(enumerated, (limit, float("inf")))] + [ +            (limit + 1, 0) +        ] +        max_v = -1 +        for (cw, cv), (nw, nv) in zip(enumerated, enumerated[1:]): +            if cw < nw and max_v < cv: +                v_append(cv) +                w_append(cw) +                max_v = cv +    return (first_v, first_w), (second_v, second_w) + + +N, W = list(map(int, input().split())) +a = [list(map(int, l.split())) for l in sys.stdin] +(first_v, first_w), (second_v, second_w) = meet_in_the_middle(a, W) +ans = 0 +for v, w in zip(first_v, first_w): +    i = bisect_right(second_w, W - w) - 1 +    new_v = v + second_v[i] +    if ans < new_v: +        ans = new_v +print(ans)