#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <bitset>
#include <array>
#include <vector>
#include <queue>
#include <set>
#include <cmath> // 変数名にy1が使えなくなるかも…。
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <limits>
#include <string>

typedef __int128_t int128_t;
std::istream &operator>>(std::istream& input, int128_t& value) { // int128_tの入力。入力が64bitに収まる前提。
    long long tmp; input >> tmp; value = tmp;
    return input;
}
std::ostream &operator<<(std::ostream& output, const int128_t value) { // int128_tの出力。出力が64bitに収まる前提。
    output << (long long)value;
    return output;
}

// binary search result
struct BSR {
    bool found; // 条件を満たす範囲が見つかったか。falseの場合、begin,endは意味を持たない。
    int128_t begin; // [begin, end]で条件が成り立つ。
    int128_t end;

    BSR(bool found_in, int128_t begin_in, int128_t end_in) : found(found_in), begin(begin_in), end(end_in) {}
};

template<class Fn> BSR binary_search(int128_t L, int128_t R, Fn func) {
    const bool res_L = func(L);
    const bool res_R = func(R);
    if (res_L && res_R) {
        return BSR(true, L, R);
    } else if (!res_L && !res_R) {
        return BSR(false, 0, 0);
    } else {
        int128_t lb = L;
        int128_t ub = R;
        while (lb + 1 < ub) {
            int128_t mid = (lb + ub) / 2;
            if (res_L == func(mid)) {
                lb = mid;
            } else {
                ub = mid;
            }
        }
        return res_L ? BSR(true, L, lb) : BSR(true, ub, R);
    }
}

int128_t largest_rectangle(const std::vector<int128_t>& h) {
    std::vector<int128_t> st, L, R;
    L.resize(h.size()); R.resize(h.size());
    for (int i = 0; i < h.size(); i++) {
        while (0 < st.size() && h[i] <= h[st.back()]) { st.pop_back(); }
        L[i] = (st.size() == 0) ? 0 : st.back() + 1;
        st.push_back(i);
    }

    st.clear();
    for (int i = h.size() - 1; 0 <= i; i--) {
        while (0 < st.size() && h[i] <= h[st.back()]) { st.pop_back(); }
        R[i] = (st.size() == 0) ? h.size() : st.back();
        st.push_back(i);
    }

    int128_t res = 0;
    for (int i = 0; i < h.size(); i++) {
        //res = std::max(res, h[i] * (R[i] - L[i]));
        res = std::max(res, (h[i] + 1) * (R[i] - L[i] + 1));
    }
    return res;
}

int H, W;
const int MAX_N = 2050;
char S[MAX_N][MAX_N];

int dw[MAX_N][MAX_N];
int d[MAX_N][MAX_N];
int d_ac[MAX_N][MAX_N];

int main(int argc, char **argv) {
    std::cin >> H >> W;

    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            std::cin >> S[i][j];
        }
    }

    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W - 1; j++) {
            dw[i][j] = (S[i][j] == S[i][j+1]) ? 0 : 1;
        }
    }

    for (int i = 0; i < H - 1; i++) {
        for (int j = 0; j < W - 1; j++) {
            d[i][j] = (dw[i][j] == dw[i+1][j]) ? 0 : 1;
            //std::cout << d[i][j] << " ";
        }
        //std::cout << std::endl;
    }

    for (int i = 0; i < H - 1; i++) {
        for (int j = 0; j < W - 1; j++) {
            d_ac[i][j] = (j == 0) ? d[i][j] : d_ac[i][j-1] + d[i][j];
            //std::cout << d[i][j] << " ";
        }
    }

    int128_t ret = std::max(H, W);
    for (int j = 0; j < W - 1; j++) {
        std::vector<int128_t> h;
        for (int i = 0; i < H - 1; i++) {
            int128_t v = 0;
            if (d[i][j] == 0) {
                auto bsr = binary_search(j, W - 2, [&](const int k){
                    return d_ac[i][k] == d_ac[i][j];
                });
                v = bsr.end - j + 1;
            }
            h.push_back(v);
        }
        //std::cout << j << std::endl;
        //for (auto k : h) {
        //    std::cout << k << " ";
        //}
        //std::cout << std::endl;
        ret = std::max(ret, largest_rectangle(h));
    }

    std::cout << ret << std::endl;

    return 0;
}


Measured memory_bytes: 53632