#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> ii;
const int INF = 1e9;
const ll LINF = 1e18;
template<typename T>
void maximize(T& a, const T& b) {
if (a < b) a = b;
}
// Ta sẽ giải quyết bài toán theo từng hàng
// Với mỗi hàng cần tính được thông tin h[j] cho mọi cột j
// h[j] = độ cao của cột thứ j = độ dài đoạn con liên tiếp dài nhất kết thúc tại a(i, j) và chỉ chứa toàn số 1
// Giả sử hình chữ nhật ta chọn phủ đoạn [l, r] -> chiều dài
// Nhận xét: chiều rộng sẽ là min{h[l..r]}
// Ở đây nếu for qua mọi đoạn [l, r] thì sẽ không đủ độ phức tạp
// Nên thay vào đó ta sẽ thử cho từng h(j) làm giá trị min (1 <= j <= m)
// rồi tìm đoạn [l, r] dài nhất chứa j thoả mãn h(j) là min của đoạn [l, r]
// - Gọi l(j) = phần tử gần nhất bên trái < h(j)
// r(j) = phần tử gần nhất bên phải < h(j)
// => [l(j) + 1, r(j) - 1] chính là đoạn dài nhất chứa j thoả mãn h(j) là min
const int N = 1e3 + 5;
int n, m;
int a[N][N];
int h[N];
int l[N];
int r[N];
int solve(int i) {
for (int j = 1; j <= m; j++) {
if (a[i][j] == 0) h[j] = 0;
else h[j]++;
}
vector<int> st;
for (int j = 1; j <= m; j++) {
while (!st.empty() && h[st.back()] >= h[j]) st.pop_back();
l[j] = st.empty() ? 0 : st.back();
st.push_back(j);
}
st.clear();
for (int j = m; j >= 1; j--) {
while (!st.empty() && h[st.back()] >= h[j]) st.pop_back();
r[j] = st.empty() ? m + 1 : st.back();
st.push_back(j);
}
int ans = 0;
for (int j = 1; j <= m; j++) {
maximize(ans, (r[j] - l[j] - 1) * h[j]);
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) cin >> a[i][j];
}
int ans = 0;
for (int i = 1; i <= n; i++) {
maximize(ans, solve(i));
}
cout << ans << '\n';
}