import java.util.*;

public class Main {
    //1.滑动窗口->双端队列,头保证范围,尾符合结果
    //2.二维矩阵的处理方法与一维的不同与相同之处
    static final int MOD = 998244353;

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        // 输入n,m,a,b
        int n = scan.nextInt();
        int m = scan.nextInt();
        int a = scan.nextInt();
        int b = scan.nextInt();

        // 输入数据
        int[][] arr = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                arr[i][j] = scan.nextInt();
            }
        }

        // 预处理每行滑动窗口的最大值和最小值
        int[][] Maxarr = new int[n][m - b + 1];
        int[][] Minarr = new int[n][m - b + 1];

        for (int i = 0; i < n; i++) {
            // 双端队列计算每行的滑动窗口的最大值
            Deque<Integer> maxDeque = new LinkedList<>();
            Deque<Integer> minDeque = new LinkedList<>();
            for (int j = 0; j < m; j++) {
                // 清理不在窗口范围内的元素
                while (!maxDeque.isEmpty() && maxDeque.peekFirst() <= j - b) {
                    maxDeque.pollFirst();
                }
                while (!minDeque.isEmpty() && minDeque.peekFirst() <= j - b) {
                    minDeque.pollFirst();
                }

                // 计算最大值
                while (!maxDeque.isEmpty() && arr[i][maxDeque.peekLast()] <= arr[i][j]) {
                    maxDeque.pollLast();
                }
                maxDeque.offerLast(j);

                // 计算最小值
                while (!minDeque.isEmpty() && arr[i][minDeque.peekLast()] >= arr[i][j]) {
                    minDeque.pollLast();
                }
                minDeque.offerLast(j);

                // 将滑动窗口的最大值和最小值记录下来
                if (j >= b - 1) {
                    //i=0,j=0开始计入
                    Maxarr[i][j - b + 1] = arr[i][maxDeque.peekFirst()];
                    Minarr[i][j - b + 1] = arr[i][minDeque.peekFirst()];
                }
            }
        }

        // 计算最终的结果
        int result = 0;
        //再一次做这个题时,双重循环的范围没有弄对,注意!!!!
        for (int i = 0; i <= n - a; i++) {
            //为什么是m-b和n-a:因为这个部分的作用对象是经过预处理的二维最大值和最小值数组
            //这俩的范围最大就是m-b和n-a
            for (int j = 0; j <= m - b; j++) {
                int max = Integer.MIN_VALUE;
                int min = Integer.MAX_VALUE;
                // 计算每个a行范围内的最大值和最小值
                //当我固定了一个列之后,在行所需要的范围内寻找最大值
                //然后得到max,再与b内的别的列进行比较,一次又一次
                //就得到了对于窗口的最大和最小值
                for (int k = i; k < i + a; k++) {
                    max = Math.max(max, Maxarr[k][j]);
                    min = Math.min(min, Minarr[k][j]);
                }
                result = (result + (max * min) % MOD) % MOD;
            }
        }

        // 输出结果
        System.out.println(result);
        // for(int i=0;i<=n-a;i++){
        //     for(int j=0;j<=m-b;j++){
        //         System.out.print(Maxarr[i][j]+" ");
        //     }
        //     System.out.println();
        // }
        // for(int i=0;i<=n-a;i++){
        //     for(int j=0;j<=m-b;j++){
        //         System.out.print(Minarr[i][j]+" ");
        //     }
        //     System.out.println();
        // }
    }
}

 

posted on 2025-05-31 10:09  fafrkvit  阅读(28)  评论(0)    收藏  举报