#include <iostream>
#include <cmath>
#include <climits>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <string>
#include <stack>
#include <algorithm>
#include <map>
#include <numeric>
#include <set>
using namespace std;
// scanf
#pragma warning(disable: 4996)
// v:vector  type:型  order:greater or less
#define _sort(v,type,order) do { sort(v.begin(),v.end(),order<type>()); } while(0)
// vector 要素の総和算出
// v:vector  default_value:初期値
#define _sum(v,default_value) accumulate(v.begin(),v.end(),default_value )
// vector 最大値( return ite )
#define _max_element(v) max_element(v.begin(),v.end())
// vector 最大値が格納されている要素値
#define _max_element_number(v) distance(v.begin(),max_element(v.begin(),v.end()))

//set<uint32_t> member; // 重複するデータを保持する事はできない member.insert(2) member.insert(2) ⇒ member.count(2)は1
//multiset<uint32_t> v; // 重複するデータも保持する事はできる   member.insert(2) member.insert(2) ⇒ member.count(2)は2

// 丸め
#define _round(v) round(v)
// 2乗 / 3乗
#define _square(v) pow(v,2)
#define _cube(v)   pow(v,3)
// 大小判定
#define _max(x,y) max(x,y)
#define _min(x,y) min(x,y)
// string ⇒ int
#define _stringtoi(s) stoi(s)
// double 平方根
#define _sqrt(x) sqrt(x)
// double 引数 x 以上で最小の整数値 ex) 3.30303 ⇒ 4
#define _ceil(x) ceil(x)
// 指定された要素以上の値が現れる最初の位置のイテレータを取得する
#define _lower_bound(v,min) lower_bound(v.begin(), v.end(), min)
// 指定された要素より大きい値が現れる最初の位置のイテレータを取得する
#define _upper_bound(v,min) upper_bound(v.begin(), v.end(), min)

// 順列 n個の数が与えられる
// 0,1,2,...,n-1
// 全ての並べ方を1行ごとに出力する
// ex 0 1 2 / 0 2 1 / 1 0 2 / 1 2 0 / 2 0 1 / 2 1 0
#define _next_permutation(v) do {   \
        for (auto num : v) {            \
                printf("%d ", num);         \
        }                               \
        printf("\n");                   \
} while (next_permutation(v.begin(), v.end()))

// 絶対値
static int32_t _abs(const int32_t x) { return  (x > 0 ? x : -x); }
// 最大公約数
static int gcd(int a, int b)
{
    if (a < b) {
        a ^= b;
        b ^= a;
        a ^= b;
    }
    return b ? gcd(b, a % b) : a;
}
// 最小公倍数
static int lcm(int a, int b) { return a * b / gcd(a, b); }
// 多次元 std::vector 生成
template<class T>
vector<T> make_vec(size_t a) { return vector<T>(a); }
template<class T, class... Ts>
auto make_vec(size_t a, Ts... ts) { return vector<decltype(make_vec<T>(ts...))>(a, make_vec<T>(ts...)); }
// ex) auto dp = make_vec<uint64_t>(SIZE + 1, 2, 2);

#if 0
/*----------------*/
string s;
for (char c : s) {
    a[c - 'a']++;
}
/*----------------*/
char(*p)[4] = (char(*)[4]) & s[0];
/*----------------*/
typedef struct _pair_t
{
    string str;
    uint32_t num;
}pair_t;

class cfind
{
    const string& str;
public:
    cfind(const string& _str) :str(_str) {}
    bool operator()(const pair_t& arg)
    {
        if (arg.str != str) { return false; }
        return true;
    }
};
/*--*/
//↑と同様の挙動をとる事が可能
map<string, uint32_t> mp;
/*----------------*/
// DFS(Depth First Search) 深さ優先探索
vector<vector<int>> G(V);
vector<bool> seen(V, false);  // 既に見たことがある頂点か記録する

stack<int> st;
st.emplace(s);  // sから探索する sは与えられる
seen[s] = true;
while (st.size() != 0) {   // 深さ優先探索
    int state = st.top();  // 現在の状態
    st.pop();
    for (auto next : G[state]) {
        if (!seen[next]) {  // 未探索の時のみ行う
            seen[next] = true;
            st.emplace(next);  //次の状態をqueueへ格納
        }
    }
}
/*----------------*/
// ビット全探索 正の数からなる要素数Nの数列が与えられ、この中からいくつか選んで合計値を計算します
/* たくさんの数式
  https://atcoder.jp/contests/abc045/tasks/arc061_a
*/
uint32_t n;
cin >> n;

uint32_t result = 0;
// i = 0からは意味ないので1からでよい
for (uint32_t i = 1; i < (1ul << n); i++) {
    vector<int> S;
    for (uint32_t j = 0; j < n; j++) {
        if (i & (1ul << j)) {
            result++;
            S.push_back(j);
        }
    }
    // S 出力結果
    // 0
    // 1
    // 0 1
    // 2
    // 0 2
    // 1 2
    // 0 1 2
}
/*----------------*/
// 動的計画法(DP)
/* ナップサック問題
https://atcoder.jp/contests/abc032/tasks/abc032_d
　
漸化式
dp[i+1][w] = max(dp[i][w - weight[i]] + value[i],dp[i][[w]) (w >= weight[i])
dp[i+1][w] = dp[i][w]                                       (w <  weight[i])
*/
// 入力
int n, W;
int weight[110], value[110];

// DPテーブル
int dp[110][10010];

int main()
{
    cin >> n >> W;
    for (int i = 0; i < n; i++) {
        cin >> value[i] >> weight[i];
    }

    // DP初期条件: dp[0][w] = 0
    for (int w = 0; w <= W; w++) {
        dp[0][w] = 0;
    }

    // DPループ
    for (int i = 0; i < n; i++) {
        for (int w = 0; w <= W; w++) {
            if (w >= weight[i]) {
                dp[i + 1][w] = max(dp[i][w - weight[i]] + value[i], dp[i][w]);
            }
            else {
                dp[i + 1][w] = dp[i][w];
            }
        }
    }

    cout << dp[n][W] << endl;
}

/*----------------*/
// 桁DP
/* 禁止された数字
https://atcoder.jp/contests/abc007/tasks/abc007_4
*/
uint64_t solve(string S)
{
    uint32_t SIZE = S.size();

    // [i] = 桁数
    // [j] = N未満フラグ
    // [k] = 特定条件フラグ 当例では "4" or "9"フラグ
    auto dp = make_vec<uint64_t>(SIZE + 1, 2, 2);

    dp[0][0][0] = 1;

    for (uint32_t i = 0; i < SIZE; i++) {
        const uint32_t digit = S[i] - '0';
        // j = 0 ⇒ j = 1 へ状態遷移する為のループ
        for (uint32_t j = 0; j < 2; j++) {
            // k = 0 ⇒ k = 1 へ状態遷移する為のループ
            for (uint32_t k = 0; k < 2; k++) {
                // 未満フラグが立っていたらNを超える事はないので 0- 9までが対象
                // 未満フラグが立っていない場合ではNを超えてはならないのでdigitまでのループ
                //*******************************************************************
                for (uint32_t d = 0; d <= (j ? 9 : digit); d++) {
                    // 4 , 9 は特定条件
                    dp[i + 1][j || (d < digit)][k || d == 4 || d == 9] += dp[i][j][k];
                    //*******************************************************************
                    //*******************************************************************
                    // 基本式
                    // for ( int d = 0; d <= ( j ? 9 : digit ); ++d )
                    //   dp[ i + 1 ][ j || ( d < digit ) ] += dp[ i ][ j ];
                    //*******************************************************************
/*
                                dp
                                [i + 1]
                                元から1なら[1] ← dp[i][1][k]
                                0なら次の桁に入れる値がdigit未満なら1
                                元から0ならdigit未満時 [1]へ ← dp[i][0][k]
                                **状態遷移をここで実現している
                                [j || (d < digit)]
                                [k || d == 4 || d == 9]
                                += dp[i][j][k];
*/
                }
            }
        }
    }
    return dp[SIZE][0][1] + dp[SIZE][1][1];
}
/*----------------*/
// しゃくとり法
// 参考文献
// https://qiita.com/drken/items/ecd1a472d3a0e7db8dce
/*
        要素数 n
        配列中、特定の部分列に含まれる要素値の和が K 以下を満たす
        連続する要素数の最大は？
*/
int main()
{
    uint32_t n;     // 要素数 n
    uint32_t K;     // 連続する要素数の和が、この値以下を満たす
    cin >> n >> K;

    vector<uint32_t> a(n);

    for (uint32_t i = 0; i < n; ++i) {
        cin >> a[i];
    }

    uint32_t result = 0;
    uint32_t right = 0;
    uint32_t sum = 0;

    // 左手は常に+1で右へ移動
    for (uint32_t left = 0; left < n; left++) {

        // 右手が動く条件  ↓であれば (sum + a[right] <= K) 部が条件
        // 要求仕様の「要素値の和が K 以下・・・」
        while ((right < n) && (sum + a[right] <= K)) {
            sum += a[right];
            // 実際にrightを1進める
            right++;
        }

        // 連続する最大要素数を記憶
        result = max(result, right - left);

        // 右手が動けなかった場合は1進める
        if (left == right) {
            right++;
        }
        // sum値から左手部を引いておく
        else {
            sum -= a[left];
        }
    }

    printf("%d\n", result);
    return 0;
}
/*----------------*/
// ARC 022B
// https://atcoder.jp/contests/arc022/tasks/arc022_2
int main()
{
    uint32_t n;     // 要素数 n
    cin >> n;

    vector<uint32_t> a(n);
    for (uint32_t i = 0; i < n; ++i) {
        cin >> a[i];
    }

    uint32_t result = 0;
    uint32_t right = 0;
    

    set<uint32_t> member;   // 重複するデータを保持する事はできない
    multiset<uint32_t> v;   // 重複するデータも保持する事はできる

    // 左手は常に+1で右へ移動
    for (uint32_t left = 0; left < n; left++) {

        // 右手が動く条件      ↓同じ値が出現していなければ右手を進める
        while ((right < n) && (member.count(a[right]) == 0)) {
            // 新規値を挿入
            member.insert(a[right]);
            // 実際にrightを1進める
            right++;
        }

        // 連続する最大要素数を記憶
        result = max(result, right - left);

        // 右手が動けなかった場合は1進める
        if (left == right) {
            right++;
        }
        // 左手部要素を削除しておく
        else {
            member.erase(a[left]);
        }
    }

    printf("%d\n", result);
    return 0;
}
#endif

int main()
{
    uint32_t a, b;     // 要素数 n
    cin >> a >> b;


    printf("%d\n", a * b);

    return 0;
}


Measured memory_bytes: 3764