CodeEff/ECCO huggingface dataset:
# Original solution
N, W = map(int, input().split())
ws = []
vs = []

for i in range(N):
	w, v = map(int, input().split())
	ws.append(w)
	vs.append(v)

w1 = ws[0]
s = 0

for i in range(N):
	ws[i] -= w1
	s += ws[i]

dp = [[[0]*(s+1) for i in range(N+1)] for k in range(N)]
for k in range(N):
	for i in range(1,N+1):
		for j in range(s+1):
			if j >= ws[i-1]:
				dp[k][i][j] = max(dp[k-1][i-1][j-ws[i-1]]+vs[i-1], dp[k][i-1][j])
			else:
				dp[k][i][j] = max(dp[k][i][j], dp[k][i-1][j])
			if j > 0:
				dp[k][i][j] = max(dp[k][i][j], dp[k][i][j-1])
ans = 0
for k in range(N):
	if W < (k+1)*w1:
		break
	ans = max(ans, dp[k][N][min(W-(k+1)*w1, s)])
print(ans)
from sys import stderr
print(dp, file=stderr)


# Optimized solution
N, W = map(int, input().split())
ws = []
vs = []

for i in range(N):
	w, v = map(int, input().split())
	ws.append(w)
	vs.append(v)

w1 = ws[0]
s = 0

for i in range(N):
	ws[i] -= w1
	s += ws[i]

dp = [[[0]*(s+1) for i in range(N+1)] for k in range(N)]
for k in range(N):
	if (k+1)*w1 > W:
		break
	for i in range(1,N+1):
		for j in range(s+1):
			if j >= ws[i-1]:
				dp[k][i][j] = max(dp[k-1][i-1][j-ws[i-1]]+vs[i-1], dp[k][i-1][j])
			else:
				dp[k][i][j] = max(dp[k][i][j], dp[k][i-1][j])
			if j > 0:
				dp[k][i][j] = max(dp[k][i][j], dp[k][i][j-1])
ans = 0
for k in range(N):
	if W < (k+1)*w1:
		break
	ans = max(ans, dp[k][N][min(W-(k+1)*w1, s)])
print(ans)
from sys import stderr
print(dp, file=stderr)
# Original solution
N,W=list(map(int,input().split()))
w0,v=list(map(int,input().split()))
goods=[[v],[],[],[]]
for i in range(N-1):
    w,v=list(map(int,input().split()))
    goods[w-w0].append(v)
val=[]
for x in range(4):
    goods[x].sort(reverse=True)
    valx=[0]*(len(goods[x])+1)
    for k in range(len(goods[x])):
        valx[k+1]=valx[k]+goods[x][k]
    val.append(valx)
ans=0
for i in range(len(goods[0])+1):
    if w0*i>W:
        break
    for j in range(len(goods[1])+1):
        if w0*(i+j)+j>W:
            break
        for k in range(len(goods[2])+1):
            if w0*(i+j+k)+j+2*k>W:
                break
            for l in range(len(goods[3])+1):
                if w0*(i+j+k+l)+j+2*k+3*l>W:
                    break
                ans=max(ans,val[0][i]+val[1][j]+val[2][k]+val[3][l])
print(ans)

# Optimized solution
N,W=list(map(int,input().split()))
goods=[[] for i in range(4)]
w1=-1
for i in range(N):
    w,v=list(map(int,input().split()))
    if i==0:
        w1=w
    goods[w-w1].append(v)
S=[]
for x in range(4):
    goods[x].sort(reverse=True)
    G=goods[x]
    now=[0]*(len(G)+1)
    for i in range(len(G)):
        now[i+1]=now[i]+G[i]
    S.append(now)
ans=0
for i in range(1+len(goods[0])):
    for j in range(1+len(goods[1])):
        for k in range(1+len(goods[2])):
            wnow=W-w1*(i+j+k)-j-2*k
            if wnow<0:
                continue
            else:
                ans=max(ans,S[0][i]+S[1][j]+S[2][k]+S[3][min(len(S[3])-1,wnow//(w1+3))])
print(ans)
# Original solution
# coding: utf-8
import array, bisect, collections, heapq, itertools, math, random, re, string, sys, time
sys.setrecursionlimit(10 ** 7)
INF = 10 ** 20
MOD = 10 ** 9 + 7
 
 
def II(): return int(eval(input()))
def ILI(): return list(map(int, input().split()))
def IAI(LINE): return [ILI() for __ in range(LINE)]
def IDI(): return {key: value for key, value in ILI()}
 
 
def solve(N, W, w, v):
    dp = [[-1] * (N + 1) for __ in range(W + 1)]
    def throw(weight=W, num=0):
        if dp[weight][num] != -1:
            return dp[weight][num]
        
        if num == N:
            return 0
        elif weight < w[num]:
            res = throw(weight, num + 1)
        else:
            res = max(throw(weight, num + 1), throw(weight - w[num], num + 1) + v[num])
        
        dp[weight][num] = res
        return res
    
    ans = throw()
    return ans
    
 
def main():
    N, W = ILI()
    w, v = [], []
    for __ in range(N):
        x, y = ILI()
        w.append(x)
        v.append(y)
    print((solve(N, W, w, v)))
 
 
if __name__ == "__main__":
    main()

# Optimized solution
# coding: utf-8
import array, bisect, collections, heapq, itertools, math, random, re, string, sys, time

sys.setrecursionlimit(10 ** 7)
INF = 10 ** 20
MOD = 10 ** 9 + 7


def II(): return int(eval(input()))
def ILI(): return list(map(int, input().split()))
def IAI(LINE): return [ILI() for __ in range(LINE)]
def IDI(): return {key: value for key, value in ILI()}


def solve(N, W, w_v):
    memo = collections.defaultdict(int)
    memo[0] = 0
    for w, v in w_v:
        for w_memo, v_memo in sorted(list(memo.items()), key=lambda x: -x[0]):
            if w + w_memo > W:
                continue
            if memo[w + w_memo] < v_memo + v:
                memo[w + w_memo] = v_memo + v

    return max(memo.values())


def main():
    N, W = ILI()
    w_v = IAI(N)
    print((solve(N, W, w_v)))


if __name__ == "__main__":
    main()
# Original solution
import sys

inf = 1 << 32

def solve():
    N, W = list(map(int, input().split()))
    ws = []
    vs = []

    for i in range(N):
        wi, vi = list(map(int, input().split()))
        ws.append(wi)
        vs.append(vi)

    dp = [[[-inf]*(3*N + 1) for j in range(N + 1)]
                              for i in range(N + 1)]

    dp[0][0][0] = 0

    for i in range(N):
        for j in range(N + 1):
            for k in range(3*N + 1):
                dp[i + 1][j][k] = dp[i][j][k]

                if j - 1 >= 0 and k - (ws[i] - ws[0]) >= 0:
                    dp[i + 1][j][k] = max(dp[i + 1][j][k], dp[i][j - 1][k - (ws[i] - ws[0])] + vs[i])

    ans = 0

    for j in range(N + 1):
        for k in range(3*N + 1):
            if ws[0]*j + k <= W:
                ans = max(ans, dp[N][j][k])

    print(ans)

if __name__ == '__main__':
    solve()

# Optimized solution
import sys

def solve():
    N, W = list(map(int, input().split()))
    v = [[] for i in range(4)]

    for i in range(N):
        wi, vi = list(map(int, input().split()))

        if i == 0:
            base = wi
            v[0].append(vi)
        else:
            v[wi - base].append(vi)

    for i in range(4):
        v[i].sort(reverse=True)

    ans = 0

    for i in range(len(v[0]) + 1):
        for j in range(len(v[1]) + 1):
            for k in range(len(v[2]) + 1):
                w = base*i + (base + 1)*j + (base + 2)*k

                if w > W:
                    continue

                l = (W - w) // (base + 3)

                kakaku = sum(v[0][:i]) + sum(v[1][:j]) + sum(v[2][:k]) + sum(v[3][:l])

                ans = max(ans, kakaku)

    print(ans)


if __name__ == '__main__':
    solve()
# Original solution
from collections import defaultdict as dd



def sol(n, w, wis, vis):
    rv = 0
    best = dd(int)
    for i, (wi, vi) in enumerate(zip(wis, vis)):
        if wi < w:
            best[(wi, i)] = max(best[(wi, i)], vi)
            rv = max(vi, rv)

    frontline = [(i, wis[i], vis[i]) for i in range(n - 1, -1, -1)]
    while frontline:
        last, cw, cv = frontline.pop()
        for i in range(last + 1, n):
            nw, nv = cw + wis[i], cv + vis[i]
            if nw <= w and best[(nw, i)] < nv:
                best[(nw, i)] = nv
                frontline.append((i, nw, nv))
                rv = max(rv, nv)

    return rv


if __name__ == "__main__":
    n, w = list(map(int, input().strip().split()))
    wis, vis = [], []
    for _ in range(n):
        wi, vi = list(map(int, input().strip().split()))
        wis.append(wi)
        vis.append(vi)
    print(sol(n, w, wis, vis))



# Optimized solution



def sol(n, w, wis, vis):
    rv = 0
    w1 = wis[0]
    coll = [[] for _ in range(n)]
    for wi, vi in zip(wis, vis):
        coll[wi - w1].append(vi)
    coll = [sorted(x, reverse=True) for x in coll]
    lis = list(map(len, coll))
    for k1 in range(min(lis[0] + 1, n + 1)):
        for k2 in range(min(lis[1] + 1, n + 1 - k1)):
            for k3 in range(min(lis[2] + 1, n + 1 - k1 - k2)):
                for k4 in range(min(lis[3] + 1, n + 1 - k1 - k2 - k3)):
                    kis = [k1, k2, k3, k4]
                    if sum([(w1 + i) * kis[i] for i in range(4)]) > w:
                        continue
                    rv = max(rv, sum([sum(coll[i][:kis[i]]) for i in range(4)]))

    return rv


if __name__ == "__main__":
    n, w = list(map(int, input().strip().split()))
    wis, vis = [], []
    for _ in range(n):
        wi, vi = list(map(int, input().strip().split()))
        wis.append(wi)
        vis.append(vi)
    print((sol(n, w, wis, vis)))
# Original solution
N,W=list(map(int,input().split()))
wv=[list(map(int,input().split())) for _ in range(N)]
w1=wv[0][0]
wm=max([i[0] for i in wv])

Wlist=[0]+[j*(w1+i) for j in range(1,N+1) for i in range(4)]
dp=[[0]*(4*N+1) for _ in range(N+1)]
#dp[i][j]:N個の物に対して強度(Wlist[j])のバッグ

for i in range(1,N+1):
  w,v=wv[i-1]
  for j in range(4*N+1):
    if w<=Wlist[j]:
      
      ind=0
      d=float("inf")
      for k in range(4*N+1):
        if (Wlist[j]-w)>=Wlist[k] and (Wlist[j]-w)-Wlist[k]<d:
          d=(Wlist[j]-w)-Wlist[k]
          ind=k
      
      dp[i][j]=max(dp[i-1][j],dp[i-1][ind]+v)
    else:
      dp[i][j]=dp[i-1][j]

ind=0
d=float("inf")
for k in range(4*N+1):
  if W>=Wlist[k] and W-Wlist[k]<d:
    d=W-Wlist[k]
    ind=k
print((dp[N][ind]))

# Optimized solution
import bisect
N,W=list(map(int,input().split()))
wv=[list(map(int,input().split())) for _ in range(N)]
w1=wv[0][0]

Wlist=[0]+[j*w1+i for j in range(1,N+1) for i in range(3*j+1)]
Wlist=sorted(set(Wlist))
LW=len(Wlist)
dp=[[0]*LW for _ in range(N+1)]
#dp[i][j]:N個の物に対して強度(Wlist[j])のバッグ

for i in range(1,N+1):
  w,v=wv[i-1]
  for j in range(LW):
    if w<=Wlist[j]:      
      ind=bisect.bisect_right(Wlist,Wlist[j]-w)-1
      dp[i][j]=max(dp[i-1][j],dp[i-1][ind]+v)
    else:
      dp[i][j]=dp[i-1][j]

ind=bisect.bisect_right(Wlist,W)-1
print((dp[N][ind]))
# Original solution
N,W = list(map(int, input().split()))
ws = []
vs = []
for i in range(N):
    w,v = list(map(int, input().split()))
    ws.append(w)
    vs.append(v)
ws_m = [w-ws[0] for w in ws]

"""
i番目まで見て、n個選んでいて、重さがwのときのvの最大値
ただし、wはN個選んだときにW-N*[w[0]]となっているので、そこをmaxとする
dp[i][n][w] 
"""
dp = [[[-1]*(3*N+10) for i in range(N+1)] for j in range(N+1)]
dp[0][0][0] = 0
for i,(w,v)  in enumerate(zip(ws_m,vs)):
    for n in range(i+1):
        for j in range(3*N):
            dp[i+1][n][j] = max(dp[i+1][n][j], dp[i][n][j])
            if dp[i][n][j] == -1: continue
            if j + (n+1)*ws[0] + w <= W:
                dp[i+1][n+1][j+w] = max(dp[i][n][j+w], dp[i][n][j] + v)

ans= 0
for i,As in enumerate(dp[N]):
    w = min(W - i*ws[0], sum(ws_m))
    if w >= 0:
        ans = max(ans,max(As[:w+1]))
print(ans)   


# Optimized solution
N,W = list(map(int, input().split()))
ws = []
vs = []
for i in range(N):
    w,v = list(map(int, input().split()))
    ws.append(w)
    vs.append(v)
ws_m = [w-ws[0] for w in ws]

"""
i番目まで見て、n個選んでいて、重さがwのときのvの最大値
ただし、wはN個選んだときにW-N*[w[0]]となっているので、そこをmaxとする
dp[i][n][w] 
"""
dp = [[-1]*(3*N+1) for i in range(N+1)]
dp[0][0] = 0
for i,(w,v)  in enumerate(zip(ws_m,vs)):    
    dp[i+1][:] = dp[i][:]
    for j in range(len(dp[i])):
        if dp[i][j] == -1:continue
        dp[i+1][j+w] = max(dp[i][j]+v, dp[i][j+w])
    # print(dp)

# print(dp[N][W-N*ws_m[0]])

dp = [[[-1]*(3*N+1) for i in range(N+1)] for j in range(N+1)]
dp[0][0][0] = 0
for i,(w,v)  in enumerate(zip(ws_m,vs)):
    # print(i,w,v)
    for n in range(N):
        # dp[i+1][n][:] = dp[i][n][:]
        for j in range(N):
            if dp[i][n][j] == -1:continue
            if dp[i][n][j]+v >= dp[i][n][j+w]:
                # print("aa",i,n,j,v)
                dp[i+1][n+1][j+w] = dp[i][n][j] + v
            else:
                dp[i+1][n][j+w] = dp[i][n][j+w]
            dp[i+1][n][j] = max(dp[i+1][n][j], dp[i][n][j])
    # print(dp[i])

# for i in range(N+1):
    # print("i個選んだやつ",dp[N][i])
anss = dp[N]
a = 0
for i,ans in enumerate(anss):
    w = W - i*ws[0]
    if w >= 0:
        a = max(a,max(ans[:w+1]))
print(a)
# Original solution
N,W = list(map(int, input().split()))
wvs = []
for i in range(N):
    w,v = list(map(int, input().split()))
    wvs.append((w,v))
# dp : n個入っていて、合計がwのときの最大値
w0 = wvs[0][0]
dp = [[[-1]*(330) for i in range(110)] for k in range(110)]
dp[0][0][0] = 0
for i in range(N):
    w, v = wvs[i]
    dw = w-w0
    for j in range(i+1):
        for k in range(310):
            # if k - dw >= 0:
            if dp[i][j][j] == -1:continue
            dp[i+1][j][k] = max(dp[i+1][j][k], dp[i][j][k])
            dp[i+1][j+1][k+dw] = max(dp[i][j][k]+v, dp[i+1][j+1][k+dw])

d = dp[N]
ans = 0
for j in range(N+1):
    if W-j*w0 >= 0:
        a = max(d[j][:W-j*w0+1])
        ans = max(ans, a)
print(ans)

# Optimized solution
N,W = list(map(int, input().split()))
wvs = []
for i in range(N):
    w,v = list(map(int, input().split()))
    wvs.append((w,v))
# dp : n個入っていて、合計がwのときの最大値
w0 = wvs[0][0]
dp = [[[-1]*(330) for i in range(110)] for k in range(110)]
dp[0][0][0] = 0
for i in range(N):
    w, v = wvs[i]
    dw = w-w0
    for j in range(i+1):
        for k in range(310):
            # if k - dw >= 0:
            # if dp[i][j][j] == -1:continue
            dp[i+1][j][k] = max(dp[i+1][j][k], dp[i][j][k])
            dp[i+1][j+1][k+dw] = max(dp[i][j][k]+v, dp[i+1][j+1][k+dw])

d = dp[N]
ans = 0
for j in range(N+1):
    if W-j*w0 >= 0:
        a = max(d[j][:W-j*w0+1])
        ans = max(ans, a)
print(ans)
# Original solution
N, W = list(map(int, input().split()))
vs = [[] for _ in range(4)]
w1, v1 = list(map(int, input().split()))
vs[0].append(v1)
for i in range(N - 1):
    w, v = list(map(int, input().split()))
    vs[w - w1].append(v)
#print(vs)
for i in range(4):
    vs[i].sort()
#print(vs)
ans = 0
for i in range(N + 1):
    if i * w1 > W:
        break
    if i == 0:
        si = 0
    else:
        si = sum(vs[0][-i:])
    for j in range(N + 1 - i):
        if i * w1 + j * (w1 + 1) > W:
            break
        sj = si
        if j > 0:
            sj += sum(vs[1][-j:])
        for k in range(N + 1 - i - j):
            if i * w1 + j * (w1 + 1) + k * (w1 + 2) > W:
                break
            sk = sj
            if k > 0:
                sk += sum(vs[2][-k:])
            for l in range(N + 1 - i - j - k):
                if i * w1 + j * (w1 + 1) + k * (w1 + 2) + l * (w1 + 3) > W:
                    break
                sl = sk               
                if l > 0:
                    sl += sum(vs[3][-l:])
                ans = max(ans, sl)
print(ans)

# Optimized solution
N, W = list(map(int, input().split()))
vs = [[] for _ in range(4)]
w1, v1 = list(map(int, input().split()))
vs[0].append(v1)
for i in range(N - 1):
    w, v = list(map(int, input().split()))
    vs[w - w1].append(v)
#print(vs)
s = [[0] for _ in range(4)]
for i in range(4):
    vs[i].sort()
    for j in range(len(vs[i])):
        s[i].append(s[i][-1] + vs[i][-j-1])
#print(vs)
ans = 0
for i in range(N + 1):
    if i > len(vs[0]):
        break
    if i * w1 > W:
        break
    si = s[0][i]
    for j in range(N + 1 - i):
        if j > len(vs[1]):
            break
        if i * w1 + j * (w1 + 1) > W:
            break
        sj = si
        sj += s[1][j]
        for k in range(N + 1 - i - j):
            if k > len(vs[2]):
                break
            if i * w1 + j * (w1 + 1) + k * (w1 + 2) > W:
                break
            sk = sj
            sk += s[2][k]
            for l in range(N + 1 - i - j - k):
                if l > len(vs[3]):
                    break
                if i * w1 + j * (w1 + 1) + k * (w1 + 2) + l * (w1 + 3) > W:
                    break
                sl = sk
                sl += s[3][l]
                ans = max(ans, sl)
print(ans)
# Original solution
import sys
input = sys.stdin.readline
from itertools import accumulate

N,W = list(map(int,input().split()))
WV = [[int(i) for i in input().split()] for _ in range(N)]

MINW = WV[0][0]

W0 = sorted([0] + [v for w,v in WV if w == MINW])
W1 = sorted([0] + [v for w,v in WV if w == MINW+1])
W2 = sorted([0] + [v for w,v in WV if w == MINW+2])
W3 = sorted([0] + [v for w,v in WV if w == MINW+3])

W0 = tuple(accumulate(W0))
W1 = tuple(accumulate(W1))
W2 = tuple(accumulate(W2))
W3 = tuple(accumulate(W3))

ans = 0
for w0 in range(len(W0)):
    for w1 in range(len(W1)):
        for w2 in range(len(W2)):
            for w3 in range(len(W3)):
                tmp = (len(W0) - (w0 + 1)) * MINW
                tmp += (len(W1) - (w1 + 1)) * (MINW+1)
                tmp += (len(W2) - (w2 + 1)) * (MINW+2)
                tmp += (len(W3) - (w3 + 1)) * (MINW+3)

                if tmp <= W:
                    tmp = W0[-1] - W0[w0]
                    tmp += W1[-1] - W1[w1]
                    tmp += W2[-1] - W2[w2]
                    tmp += W3[-1] - W3[w3]
                    ans = max(ans, tmp)

print(ans)

# Optimized solution
import sys
input = sys.stdin.readline
from itertools import accumulate

N,W = list(map(int,input().split()))
WV = [[int(i) for i in input().split()] for _ in range(N)]

MINW = WV[0][0]

# それぞれの重さについての価値のリスト
W0 = sorted([0] + [v for w,v in WV if w == MINW])
W1 = sorted([0] + [v for w,v in WV if w == MINW+1])
W2 = sorted([0] + [v for w,v in WV if w == MINW+2])
W3 = sorted([0] + [v for w,v in WV if w == MINW+3])

# 累積和
W0 = tuple(accumulate(W0))
W1 = tuple(accumulate(W1))
W2 = tuple(accumulate(W2))
W3 = tuple(accumulate(W3))

ans = 0
for w0 in range(len(W0)):
    for w1 in range(len(W1)):
        for w2 in range(len(W2)):
            for w3 in range(len(W3)):
                # tmp: 状態(w0,w1,w2,w3)での重さ
                tmp = (len(W0) - (w0 + 1)) * MINW
                tmp += (len(W1) - (w1 + 1)) * (MINW+1)
                tmp += (len(W2) - (w2 + 1)) * (MINW+2)
                tmp += (len(W3) - (w3 + 1)) * (MINW+3)

                # ナップサックに入る場合
                if tmp <= W:
                    # tmp: 価値の総和
                    tmp = W0[-1] - W0[w0]
                    tmp += W1[-1] - W1[w1]
                    tmp += W2[-1] - W2[w2]
                    tmp += W3[-1] - W3[w3]
                    ans = max(ans, tmp)

print(ans)
# Original solution
import sys
input = sys.stdin.readline
from itertools import accumulate

N,W = list(map(int,input().split()))
WV = [[int(i) for i in input().split()] for _ in range(N)]

MINW = WV[0][0]

# それぞれの重さについての価値のリスト
W0 = sorted([0] + [v for w,v in WV if w == MINW])
W1 = sorted([0] + [v for w,v in WV if w == MINW+1])
W2 = sorted([0] + [v for w,v in WV if w == MINW+2])
W3 = sorted([0] + [v for w,v in WV if w == MINW+3])

# 累積和
W0 = tuple(accumulate(W0))
W1 = tuple(accumulate(W1))
W2 = tuple(accumulate(W2))
W3 = tuple(accumulate(W3))

ans = 0
for w0 in range(len(W0)):
    for w1 in range(len(W1)):
        for w2 in range(len(W2)):
            for w3 in range(len(W3)):
                # tmp: 状態(w0,w1,w2,w3)での重さ
                tmp = (len(W0) - (w0 + 1)) * MINW
                tmp += (len(W1) - (w1 + 1)) * (MINW+1)
                tmp += (len(W2) - (w2 + 1)) * (MINW+2)
                tmp += (len(W3) - (w3 + 1)) * (MINW+3)

                # ナップサックに入る場合
                if tmp <= W:
                    # tmp: 価値の総和
                    tmp = W0[-1] - W0[w0]
                    tmp += W1[-1] - W1[w1]
                    tmp += W2[-1] - W2[w2]
                    tmp += W3[-1] - W3[w3]
                    ans = max(ans, tmp)

print(ans)

# Optimized solution
N,W = list(map(int, input().split()))
WV = [[int(i) for i in input().split()] for _ in range(N)]

MINW = WV[0][0]

W0,W1,W2,W3 = [],[],[],[]
for w,v in WV:
    if w == MINW: W0.append(v)
    elif w == MINW + 1: W1.append(v)
    elif w == MINW + 2: W2.append(v)
    elif w == MINW + 3: W3.append(v)

W0.sort(reverse=True)
W1.sort(reverse=True)
W2.sort(reverse=True)
W3.sort(reverse=True)

W0 = [0] + W0
W1 = [0] + W1
W2 = [0] + W2
W3 = [0] + W3

from itertools import accumulate

W0 = list(accumulate(W0))
W1 = list(accumulate(W1))
W2 = list(accumulate(W2))
W3 = list(accumulate(W3))

ans = 0
for cnt0 in range(len(W0)):
    for cnt1 in range(len(W1)):
        for cnt2 in range(len(W2)):
            for cnt3 in range(len(W3)):
                SUMW = cnt0 * MINW
                SUMW += cnt1 * (MINW + 1)
                SUMW += cnt2 * (MINW + 2)
                SUMW += cnt3 * (MINW + 3)

                if SUMW <= W:
                    SUMV = W0[cnt0]
                    SUMV += W1[cnt1]
                    SUMV += W2[cnt2]
                    SUMV += W3[cnt3]
                    ans = max(ans, SUMV)

print(ans)
# Original solution
def solve():
    from itertools import accumulate

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

    e = tuple([] for _ in range(4))
    w1, v1 = list(map(int, input().split()))
    e[0].append(v1)

    for _ in range(N - 1):
        _w, _v = list(map(int, input().split()))
        e[_w - w1].append(_v)

    g = [None for _ in range(4)]
    for j in range(4):
        e[j].sort(reverse=True)
        g[j] = (0,) + tuple(accumulate(e[j]))

    ret = 0
    for a in range(min(len(e[0]), N) + 1):
        for b in range(min(len(e[1]), N - a) + 1):
            for c in range(min(len(e[2]), N - a - b) + 1):
                for d in range(min(len(e[3]), N - a - b - c) + 1):
                    if w1 * a + (w1 + 1) * b + (w1 + 2) * c + (w1 + 3) * d > W:
                        break
                    ret = max(ret, g[0][a] + g[1][b] + g[2][c] + g[3][d])
    return ret


print((solve()))


# Optimized solution
def solve():
    from itertools import accumulate

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

    e = tuple([] for _ in range(4))
    w1, v1 = list(map(int, input().split()))
    e[0].append(v1)

    for _ in range(N - 1):
        _w, _v = list(map(int, input().split()))
        e[_w - w1].append(_v)

    g = [None for _ in range(4)]
    for j in range(4):
        e[j].sort(reverse=True)
        g[j] = (0,) + tuple(accumulate(e[j]))

    ret = 0
    for a in range(min(len(e[0]), N) + 1):
        for b in range(min(len(e[1]), N - a) + 1):
            for c in range(min(len(e[2]), N - a - b) + 1):
                diff = W - (w1 * a + (w1 + 1) * b + (w1 + 2) * c)
                if diff < 0:
                    break
                d = min(len(e[3]), diff // (w1 + 3))
                ret = max(ret, g[0][a] + g[1][b] + g[2][c] + g[3][d])
    return ret


print((solve()))

# https://atcoder.jp/contests/arc073/submissions/2794675