import java.util.*;

/**
 * NC237 最大矩形
 * @author d3y1
 */
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param matrix int整型二维数组
     * @return int整型
     */
    public int maximalRectangle (int[][] matrix) {
        return solution(matrix);
    }

    /**
     * 动态规划 + 单调栈
     *
     * heights[i][j]表示以第i行为底的矩形图的第j列的高度
     *
     *                 { 0                    , matrix[i-1][j-1] == 0
     * heights[i][j] = {
     *                 { heights[i-1][j] + 1  , matrix[i-1][j-1] == 1
     *
     *
     * matrix
     * i\j 1 2 3 4 5
     * 1   1 0 1 0 0
     * 2   1 1 1 1 0
     * 3   1 1 1 1 1
     * 4   1 0 0 1 0
     *
     * heights[i][j]
     * i\j 1 2 3 4 5
     * 1   1 0 1 0 0
     * 2   2 1 2 1 0
     * 3   3 2 3 2 1
     * 4   4 0 0 3 0
     *
     * @param matrix
     * @return
     */
    private int solution(int[][] matrix){
        int n = matrix.length;
        int m = matrix[0].length;

        int[][] heights = new int[n+1][m+1];

        // 以第i行为底的矩形图的第j列的高度
        for(int i=1; i<=n; i++){
            for(int j=1; j<=m; j++){
                if(matrix[i-1][j-1] == 1){
                    heights[i][j] = heights[i-1][j] + 1;
                }
            }
        }

        Stack<Integer> leftStack;
        Stack<Integer> rightStack;
        HashMap<Integer, Integer> leftMap;
        HashMap<Integer, Integer> rightMap;

        int area = 0;
        // 分别计算以各行为底的矩形图
        for(int i=1; i<=n; i++){
            leftStack = new Stack<>();
            rightStack = new Stack<>();
            leftMap = new HashMap<>();
            rightMap = new HashMap<>();
            int height;
            int index;
            // 单调栈
            for(int j=1; j<=m; j++){
                height = heights[i][j];
                // 单调增 从左往右 查找左边第一个小于当前元素的索引 0-左边界(表示未找到)
                while(!leftStack.isEmpty() && height<=heights[i][leftStack.peek()]){
                    leftStack.pop();
                }
                index = leftStack.isEmpty()?0:leftStack.peek();
                leftMap.put(j, index);
                leftStack.push(j);
            }
            // 单调栈
            for(int j=m; j>0; j--){
                height = heights[i][j];
                // 单调增 从右往左 查找右边第一个小于当前元素的索引 (m+1)-右边界(表示未找到)
                while(!rightStack.isEmpty() && height<=heights[i][rightStack.peek()]){
                    rightStack.pop();
                }
                index = rightStack.isEmpty()?m+1:rightStack.peek();
                rightMap.put(j, index);
                rightStack.push(j);
            }
            
            // 计算当前矩形图 最大矩形面积
            for(int j=1; j<=m; j++){
                area = Math.max(area, heights[i][j]*(rightMap.get(j)-leftMap.get(j)-1));
            }
        }

        return area;
    }
}