// FROM CHATGPT : cay qua khong AC duoc nen em moi lam lieu nha dung go em <(")
#pragma GCC optimize("Ofast,unroll-loops")
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
if (fopen("TABLE3.INP","r")){
freopen("TABLE3.INP","r",stdin);
freopen("TABLE3.OUT","w",stdout);
}
int n,m;
if(!(cin>>n>>m)) return 0;
vector<vector<int>> a(n+1, vector<int>(m+1));
vector<int> vals; vals.reserve(n*m);
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ cin>>a[i][j]; vals.push_back(a[i][j]); }
sort(vals.begin(), vals.end());
vals.erase(unique(vals.begin(), vals.end()), vals.end());
auto get_id = [&](int x)->int{ return lower_bound(vals.begin(), vals.end(), x) - vals.begin(); };
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j] = get_id(a[i][j]);
int V = (int)vals.size();
int MULT = n+1;
int SZ = MULT * MULT;
vector<int> mx1(SZ,0), mx2(SZ,0), mx_block(SZ,0);
ll ans = 0;
const long long FLAT_LIMIT = 50000000LL;
bool use_flat = (1LL * V * MULT <= FLAT_LIMIT);
vector<int> lastpos_flat;
unordered_map<int,int> lastpos_map;
if(use_flat){ lastpos_flat.assign(V * MULT, 0); }
else { lastpos_map.reserve(n*m*2 + 10); }
auto lastpos_get = [&](int v, int row)->int{
int key = v * MULT + row;
if(use_flat) return lastpos_flat[key];
auto it = lastpos_map.find(key);
return (it==lastpos_map.end()?0:it->second);
};
auto lastpos_set = [&](int v, int row, int val){
int key = v * MULT + row;
if(use_flat) lastpos_flat[key] = val;
else lastpos_map[key] = val;
};
vector<int> last_in_col(V,0), prev_same(n+1,0), prefMax(n+1,0);
for(int k=1;k<=m;k++){
fill(last_in_col.begin(), last_in_col.end(), 0);
for(int r=1;r<=n;r++){
int v = a[r][k];
prev_same[r] = last_in_col[v];
last_in_col[v] = r;
prefMax[r] = prefMax[r-1] > prev_same[r] ? prefMax[r-1] : prev_same[r];
}
for(int r=1;r<=n;r++){
int cur = 0;
int base1 = r * MULT;
int vr = a[r][k];
for(int s=r;s>=1;--s){
int last = lastpos_get(vr, s);
if(last > cur) cur = last;
mx1[base1 + s] = cur;
}
}
for(int r=1;r<=n;r++){
int cur = 0;
int base2 = r * MULT;
int vr = a[r][k];
for(int s=r;s<=n;++s){
int last = lastpos_get(vr, s);
if(last > cur) cur = last;
mx2[base2 + s] = cur;
}
}
for(int j=1;j<=n;j++){
int jm = j * MULT;
for(int i=j+1;i<=n;i++){
int idx1 = (i-1)*MULT + j;
int idx2 = i*MULT + j;
if(mx1[idx1] > mx1[idx2]) mx1[idx2] = mx1[idx1];
}
}
for(int j=n;j>=1;--j){
for(int i=j-1;i>=1;--i){
int idx1 = (i+1)*MULT + j;
int idx2 = i*MULT + j;
if(mx2[idx1] > mx2[idx2]) mx2[idx2] = mx2[idx1];
}
}
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
int &mx = mx_block[i*MULT + j];
if(!(prefMax[j] < i)) mx = k;
int t1 = mx1[j*MULT + i];
if(t1 > mx) mx = t1;
int t2 = mx2[i*MULT + j];
if(t2 > mx) mx = t2;
int width = k - mx;
if(width > 0){
ll area = 1ll * (j - i + 1) * width;
if(area > ans) ans = area;
}
}
}
for(int r=1;r<=n;r++) lastpos_set(a[r][k], r, k);
fill(prefMax.begin(), prefMax.end(), 0);
}
cout << ans;
return 0;
}