#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 1 2 4 1 3 10 1 4 4 1 5 10 1 6 8 1 7 20 1 8 17 2 3 2 2 4 16 2 5 26 2 6 20 2 7 40 2 8 25 3 4 26 3 5 36 3 6 26 3 7 50 3 8 25 4 5 2 4 6 4 4 7 8 4 8 17 5 6 2 5 7 2 5 8 13 6 7 4 6 8 5 7 8 13 */ template std::ostream &operator<<(std::ostream &os, const std::pair &p) { os << p.first << " " << p.second; return os; } template std::istream &operator>>(std::istream &is, std::pair &p) { is >> p.first >> p.second; return is; } template std::ostream &operator<<(std::ostream &os, const std::vector &v) { for (int i = 0; i < (int)v.size(); i++) { os << v[i] << (i + 1 != (int)v.size() ? " " : ""); } return os; } template std::istream &operator>>(std::istream &is, std::vector &v) { for (T &in : v) is >> in; return is; } template struct ModInt { int x; ModInt() : x(0) {} ModInt(long long y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {} ModInt &operator+=(const ModInt &p) { if ((x += p.x) >= mod) x -= mod; return *this; } ModInt &operator-=(const ModInt &p) { if ((x += mod - p.x) >= mod) x -= mod; return *this; } ModInt &operator*=(const ModInt &p) { x = (int)(1LL * x * p.x % mod); return *this; } ModInt &operator/=(const ModInt &p) { *this *= p.inverse(); return *this; } ModInt &operator^=(long long p) { // quick_pow here:3 ModInt res = 1; for (; p; p >>= 1) { if (p & 1) res *= *this; *this *= *this; } return *this = res; } ModInt operator-() const { return ModInt(-x); } ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; } ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; } ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; } ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; } ModInt operator^(long long p) const { return ModInt(*this) ^= p; } bool operator==(const ModInt &p) const { return x == p.x; } bool operator!=(const ModInt &p) const { return x != p.x; } explicit operator int() const { return x; } // added by QCFium ModInt operator=(const int p) { x = p; return ModInt(*this); } // added by QCFium ModInt inverse() const { int a = x, b = mod, u = 1, v = 0, t; while (b > 0) { t = a / b; a -= t * b; std::swap(a, b); u -= t * v; std::swap(u, v); } return ModInt(u); } friend std::ostream &operator<<(std::ostream &os, const ModInt &p) { return os << p.x; } friend std::istream &operator>>(std::istream &is, ModInt &a) { long long x; is >> x; a = ModInt(x); return (is); } }; using mint = ModInt<1000000007>; // using mint = ModInt<998244353>; const int MOD = 998244353; template std::pair, std::vector> get_prime_factor_with_kinds(T n) { std::vector prime_factors; std::vector cnt; // number of i_th factor for (T i = 2; i * i <= n; i++) { if (n % i == 0) { prime_factors.push_back(i); cnt.push_back(0); while (n % i == 0) n /= i, cnt[(int)prime_factors.size() - 1]++; } } if (n > 1) prime_factors.push_back(n), cnt.push_back(1); assert(prime_factors.size() == cnt.size()); return {prime_factors, cnt}; } /* Range Add Range Sum struct S { long long value; int size; }; using F = long long; S op(S a, S b) { return {a.value + b.value, a.size + b.size}; } S e() { return {0, 0}; } S mapping(F f, S x) { return {x.value + f * x.size, x.size}; } F composition(F f, F g) { return f + g; } F id() { return 0; } Ranged affine range sum struct S { mint sum; int len; }; struct F { mint a; mint b; }; S op(S l, S r) { return {l.sum + r.sum, l.len + r.len}; } S e() { return {0, 0}; } S mapping(F lazy, S node) { return {node.sum * lazy.a + node.len * lazy.b, node.len}; } F composition(F f, F g) { // f is son, g is parent return {f.a * g.a, g.b * f.a + f.b}; } F id() { return {1, 0}; } */ // #pragma once #include #include #include #include namespace atcoder { namespace internal { #if __cplusplus >= 202002L using std::bit_ceil; #else // @return same with std::bit::bit_ceil unsigned int bit_ceil(unsigned int n) { unsigned int x = 1; while (x < (unsigned int)(n)) x *= 2; return x; } #endif // @param n `1 <= n` // @return same with std::bit::countr_zero int countr_zero(unsigned int n) { #ifdef _MSC_VER unsigned long index; _BitScanForward(&index, n); return index; #else return __builtin_ctz(n); #endif } // @param n `1 <= n` // @return same with std::bit::countr_zero constexpr int countr_zero_constexpr(unsigned int n) { int x = 0; while (!(n & (1 << x))) x++; return x; } } // namespace internal } // namespace atcoder namespace atcoder { #if __cplusplus >= 201703L template struct lazy_segtree { static_assert(std::is_convertible_v>, "op must work as S(S, S)"); static_assert(std::is_convertible_v>, "e must work as S()"); static_assert( std::is_convertible_v>, "mapping must work as F(F, S)"); static_assert( std::is_convertible_v>, "compostiion must work as F(F, F)"); static_assert(std::is_convertible_v>, "id must work as F()"); #else template struct lazy_segtree { #endif public: lazy_segtree() : lazy_segtree(0) {} explicit lazy_segtree(int n) : lazy_segtree(std::vector(n, e())) {} explicit lazy_segtree(const std::vector &v) : _n(int(v.size())) { size = (int)internal::bit_ceil((unsigned int)(_n)); log = internal::countr_zero((unsigned int)size); d = std::vector(2 * size, e()); lz = std::vector(size, id()); for (int i = 0; i < _n; i++) d[size + i] = v[i]; for (int i = size - 1; i >= 1; i--) { update(i); } } void set(int p, S x) { assert(0 <= p && p < _n); p += size; for (int i = log; i >= 1; i--) push(p >> i); d[p] = x; for (int i = 1; i <= log; i++) update(p >> i); } S get(int p) { assert(0 <= p && p < _n); p += size; for (int i = log; i >= 1; i--) push(p >> i); return d[p]; } S prod(int l, int r) { assert(0 <= l && l <= r && r <= _n); if (l == r) return e(); l += size; r += size; for (int i = log; i >= 1; i--) { if (((l >> i) << i) != l) push(l >> i); if (((r >> i) << i) != r) push((r - 1) >> i); } S sml = e(), smr = e(); while (l < r) { if (l & 1) sml = op(sml, d[l++]); if (r & 1) smr = op(d[--r], smr); l >>= 1; r >>= 1; } return op(sml, smr); } S all_prod() { return d[1]; } void apply(int p, F f) { assert(0 <= p && p < _n); p += size; for (int i = log; i >= 1; i--) push(p >> i); d[p] = mapping(f, d[p]); for (int i = 1; i <= log; i++) update(p >> i); } void apply(int l, int r, F f) { assert(0 <= l && l <= r && r <= _n); if (l == r) return; l += size; r += size; for (int i = log; i >= 1; i--) { if (((l >> i) << i) != l) push(l >> i); if (((r >> i) << i) != r) push((r - 1) >> i); } { int l2 = l, r2 = r; while (l < r) { if (l & 1) all_apply(l++, f); if (r & 1) all_apply(--r, f); l >>= 1; r >>= 1; } l = l2; r = r2; } for (int i = 1; i <= log; i++) { if (((l >> i) << i) != l) update(l >> i); if (((r >> i) << i) != r) update((r - 1) >> i); } } template int max_right(int l) { return max_right(l, [](S x) { return g(x); }); } template int max_right(int l, G g) { assert(0 <= l && l <= _n); assert(g(e())); if (l == _n) return _n; l += size; for (int i = log; i >= 1; i--) push(l >> i); S sm = e(); do { while (l % 2 == 0) l >>= 1; if (!g(op(sm, d[l]))) { while (l < size) { push(l); l = (2 * l); if (g(op(sm, d[l]))) { sm = op(sm, d[l]); l++; } } return l - size; } sm = op(sm, d[l]); l++; } while ((l & -l) != l); return _n; } template int min_left(int r) { return min_left(r, [](S x) { return g(x); }); } template int min_left(int r, G g) { assert(0 <= r && r <= _n); assert(g(e())); if (r == 0) return 0; r += size; for (int i = log; i >= 1; i--) push((r - 1) >> i); S sm = e(); do { r--; while (r > 1 && (r % 2)) r >>= 1; if (!g(op(d[r], sm))) { while (r < size) { push(r); r = (2 * r + 1); if (g(op(d[r], sm))) { sm = op(d[r], sm); r--; } } return r + 1 - size; } sm = op(d[r], sm); } while ((r & -r) != r); return 0; } private: int _n, size, log; std::vector d; std::vector lz; void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); } void all_apply(int k, F f) { d[k] = mapping(f, d[k]); if (k < size) lz[k] = composition(f, lz[k]); } void push(int k) { all_apply(2 * k, lz[k]); all_apply(2 * k + 1, lz[k]); lz[k] = id(); } }; } // namespace atcoder using S = long long; using F = long long; const S INF = 8e18; S op(S a, S b) { return std::max(a, b); } S e() { return -INF; } S mapping(F f, S x) { return f + x; } F composition(F f, F g) { return f + g; } F id() { return 0; } double get_e(double x1, double y1, double x2, double y2) { return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); } // knight move template struct DoublingLowestCommonAncestor { std::vector>> g; std::vector dep; std::vector sum; std::vector> table; const int LOG; explicit DoublingLowestCommonAncestor(int n) : g(n), LOG(32 - __builtin_clz(n)) {} void add_edge(int u, int v, T w = 1) { g[u].emplace_back(v, w); g[v].emplace_back(u, w); } void build(int root = 0) { dep.assign(g.size(), 0); sum.assign(g.size(), 0); table.assign(LOG, std::vector(g.size(), -1)); dfs(root, -1, 0); for (int k = 0; k + 1 < LOG; k++) { for (int i = 0; i < (int)table[k].size(); i++) { if (table[k][i] == -1) table[k + 1][i] = -1; else table[k + 1][i] = table[k][table[k][i]]; } } } int lca(int u, int v) { if (dep[u] > dep[v]) std::swap(u, v); v = climb(v, dep[v] - dep[u]); if (u == v) return u; for (int i = LOG - 1; i >= 0; i--) { if (table[i][u] != table[i][v]) { u = table[i][u]; v = table[i][v]; } } return table[0][u]; } int climb(int u, int k) { if (dep[u] < k) return -1; for (int i = LOG - 1; i >= 0; i--) { if ((k >> i) & 1) u = table[i][u]; } return u; } T dist(int u, int v) { return sum[u] + sum[v] - 2 * sum[lca(u, v)]; } private: void dfs(int idx, int par, int d) { table[0][idx] = par; dep[idx] = d; for (auto &to : g[idx]) { if (to.first != par) { sum[to.first] = sum[idx] + to.second; dfs(to.first, idx, d + 1); } } } }; template struct DSU { std::vector f, siz; DSU(int n) : f(n), siz(n, 1) { std::iota(f.begin(), f.end(), 0); } T leader(T x) { while (x != f[x]) x = f[x] = f[f[x]]; return x; } bool same(T x, T y) { return leader(x) == leader(y); } bool merge(T x, T y) { x = leader(x); y = leader(y); if (x == y) return false; siz[x] += siz[y]; f[y] = x; return true; } T size(int x) { return siz[leader(x)]; } }; // credit atcoder // https://github.com/atcoder/ac-library/blob/master/document_en/mincostflow.md namespace internal { template struct simple_queue { std::vector payload; int pos = 0; void reserve(int n) { payload.reserve(n); } int size() const { return int(payload.size()) - pos; } bool empty() const { return pos == int(payload.size()); } void push(const T &t) { payload.push_back(t); } T &front() { return payload[pos]; } void clear() { payload.clear(); pos = 0; } void pop() { pos++; } }; } // namespace internal template struct mf_graph { public: mf_graph() : _n(0) {} mf_graph(int n) : _n(n), g(n) {} int add_edge(int from, int to, Cap cap) { assert(0 <= from && from < _n); assert(0 <= to && to < _n); assert(0 <= cap); int m = int(pos.size()); pos.push_back({from, int(g[from].size())}); g[from].push_back(_edge{to, int(g[to].size()), cap}); g[to].push_back(_edge{from, int(g[from].size()) - 1, 0}); return m; } struct edge { int from, to; Cap cap, flow; }; edge get_edge(int i) { int m = int(pos.size()); assert(0 <= i && i < m); auto _e = g[pos[i].first][pos[i].second]; auto _re = g[_e.to][_e.rev]; return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap}; } std::vector edges() { int m = int(pos.size()); std::vector result; for (int i = 0; i < m; i++) { result.push_back(get_edge(i)); } return result; } void change_edge(int i, Cap new_cap, Cap new_flow) { int m = int(pos.size()); assert(0 <= i && i < m); assert(0 <= new_flow && new_flow <= new_cap); auto &_e = g[pos[i].first][pos[i].second]; auto &_re = g[_e.to][_e.rev]; _e.cap = new_cap - new_flow; _re.cap = new_flow; } Cap flow(int s, int t) { return flow(s, t, std::numeric_limits::max()); } Cap flow(int s, int t, Cap flow_limit) { assert(0 <= s && s < _n); assert(0 <= t && t < _n); std::vector level(_n), iter(_n); internal::simple_queue que; auto bfs = [&]() { std::fill(level.begin(), level.end(), -1); level[s] = 0; que.clear(); que.push(s); while (!que.empty()) { int v = que.front(); que.pop(); for (auto e : g[v]) { if (e.cap == 0 || level[e.to] >= 0) continue; level[e.to] = level[v] + 1; if (e.to == t) return; que.push(e.to); } } }; auto dfs = [&](auto self, int v, Cap up) { if (v == s) return up; Cap res = 0; int level_v = level[v]; for (int &i = iter[v]; i < int(g[v].size()); i++) { _edge &e = g[v][i]; if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue; Cap d = self(self, e.to, std::min(up - res, g[e.to][e.rev].cap)); if (d <= 0) continue; g[v][i].cap += d; g[e.to][e.rev].cap -= d; res += d; if (res == up) break; } return res; }; Cap flow = 0; while (flow < flow_limit) { bfs(); if (level[t] == -1) break; std::fill(iter.begin(), iter.end(), 0); while (flow < flow_limit) { Cap f = dfs(dfs, t, flow_limit - flow); if (!f) break; flow += f; } } return flow; } std::vector min_cut(int s) { std::vector visited(_n); internal::simple_queue que; que.push(s); while (!que.empty()) { int p = que.front(); que.pop(); visited[p] = true; for (auto e : g[p]) { if (e.cap && !visited[e.to]) { visited[e.to] = true; que.push(e.to); } } } return visited; } private: int _n; struct _edge { int to, rev; Cap cap; }; std::vector> pos; std::vector> g; }; template struct FordFulkerson { struct Edge { int dst, rev; T cap; explicit Edge(const int dst, const T cap, const int rev) : dst(dst), rev(rev), cap(cap) {} }; std::vector> graph; explicit FordFulkerson(const int n) : graph(n), timestamp(0), is_used(n, -1) {} void add_edge(const int src, const int dst, const T cap) { graph[src].emplace_back(dst, cap, graph[dst].size()); graph[dst].emplace_back(src, 0, graph[src].size() - 1); } T maximum_flow(const int s, const int t, T limit = std::numeric_limits::max()) { T res = 0; while (limit > 0) { std::cout << "limit: " << limit << '\n'; const T tmp = dfs(s, t, limit); ++timestamp; if (tmp == 0) break; limit -= tmp; res += tmp; } return res; } private: int timestamp; std::vector is_used; T dfs(const int ver, const int t, const T flow) { if (ver == t) return flow; is_used[ver] = timestamp; for (Edge &e : graph[ver]) { if (is_used[e.dst] < timestamp && e.cap > 0) { const T tmp = dfs(e.dst, t, std::min(flow, e.cap)); if (tmp > 0) { e.cap -= tmp; graph[e.dst][e.rev].cap += tmp; return tmp; } } } return 0; } }; void solve() { int n, w; std::cin >> n >> w; std::vector> item(n); // value, weights for (int i = 0; i < n; i++) { int a, b; std::cin >> a >> b; item[i] = {a, b}; } long long ans = 0; std::sort(item.rbegin(), item.rend()); // sort items decreasing order in value std::vector dp(n + 1, std::vector(w + 1, 0)); for (int i = 0; i < n; i++) { for (int j = 0; j <= w; j++) { auto [value, weight] = item[i]; dp[i + 1][j] = std::max(dp[i + 1][j], dp[i][j]); if (j - weight >= 0) { if (dp[i + 1][j] < dp[i][j - weight] + value) { dp[i + 1][j] = dp[i][j - weight] + value; // item i is selected ans = std::max(ans, dp[i + 1][j] * value); // update our answer, we know the value is the minimum value in the // knapsack because of previous sorting } } } } std::cout << ans << '\n'; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int t = 1; // std::cin >> t; while (t--) { solve(); } }