【矩阵】求01矩阵中,只含1的子矩阵的个数

例题:P5300 [GXOI/GZOI2019]与或和

题解:参考资料

 


有一个显然的结论:

以(n,m)为右下角的所有子矩阵之和 = n*m


枚举每一个点,我们来计算以它为右下角的子矩阵个数 对于一般情况,在2处,A区域就没有意义,4处,B区域就没有意义 【矩阵】求01矩阵中,只含1的子矩阵的个数_第1张图片 

所以我们要维护一个单调栈,栈中元素k要满足s[i][k]单调递增(栈顶s[i][k]最大)

每次有元素出栈时,说明有部分答案不能产生贡献,见代码注释

以上是别人的说法

【个人见解】:

1、预处理(容易理解)

2、单调栈维护 距离 ,以便求出 落差×距离

落差×距离 = 不作贡献的个数

当我们在求(i,j)为右下角的时候,我们是把所有的子矩阵算入其中(还需要看高度)

然而有不作贡献的存在,譬如说当前枚举的高度对于前面的都要低。

∴需要单调栈维护的距离 剔除 一些没有这个高度的子矩阵个数。


其实这样说还是有点欠缺的。

 

大家看一个事例吧。

【矩阵】求01矩阵中,只含1的子矩阵的个数_第2张图片

预处理后得到:

【矩阵】求01矩阵中,只含1的子矩阵的个数_第3张图片

在枚举第四行的时候:

初始化:cnt = 0


1、首先第一列:cnt  = cnt + 2     cnt = 2

这样的话得到的是以(4,1)为右下角的子矩阵有2个。


2、然后第二列:cnt = cnt + 2      cnt = 4

这样的话得到是是以(4,2)为右下角的子矩阵有4个。

 

结论 :

以(n,m)为右下角的所有子矩阵之和 = n*m

其实也相当于:(高度,宽度)

子矩阵的个数 = 高度 × 宽度

 

在高度相等的情况下,cnt = cnt + 2,等价于(高度不变,宽度+1)

高度(2) *宽度 (1+1)  = 4


第三列:cnt = cnt + 4

 

这样的话得到是是以(4,3)为右下角的子矩阵有8个。

这个是'<'的情况,就是说高于前一个。

这种情况可以分解成   当前值  4=2(前面高度)+2(额外高度);

高度(2)×宽度(2+1)+额外高度(2) = 2*3+2 = 8;

 


第4列:cnt = cnt + 1           ,cnt = 9

 

cnt = cnt  -(第一高度差(3)×距离(1) ) = 9 - ( 3 * 1 ) = 6 

cnt = cnt  -(第二高度差(1)×距离(2) ) = 6 - ( 1 * 2 ) = 4

这样的话得到是是以(4,4)为右下角的子矩阵有4个。

这个是'>'的情况,就是说低于前一个。

需要维护,当前值为最低时,我们要剔除之前统计过的

cnt = cnt - (高度差×相应距离)

 


#include
using namespace std;
const int N = 1e3+10;
int a[N][N],A[N][N];                    //A[][] 原数组 , a[][]处理后的数组
int Stk[N],top;                         //单调栈,单调递增
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);                //输入
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&A[i][j]);
        }
    }

    for(int i=1;i<=n;i++){              //预处理
        for(int j=1;j<=n;j++){
            if( A[i][j] ) a[i][j] = a[i-1][j] + 1;
            else          a[i][j] = 0;
        }
    }

    long long ans = 0,cnt =0;
    for(int i=1;i<=n;i++){
        top = cnt = 0;      //单调栈(递增),栈存的是每一行的列坐标
        for(int j=1;j<=m;j++){
            cnt += a[i][j];
            while( top && a[i][j] <= a[i][Stk[top]] ) {
                cnt -= ( Stk[top] - Stk[top-1] ) * ( a[i][Stk[top]] - a[i][j] );
                //(栈顶元素) - (第二大的元素)   =  距离
                //(栈顶元素) - (当前元素)  = 差值
                // 距离 × 差值 = 不作贡献的个数
                top--;
            }
            ans += cnt;
            Stk[++top] = j;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
/*

5 5
0 0 1 0 0
1 0 1 0 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1

107
*/

#include
using namespace std;
typedef long long ll;
const int N = 1005;
const ll mod = 1e9+7;
ll A[N][N],a[N][N],b[N][N];
ll cnt1,cnt2,ans1,ans2;
ll n,St[N],top,tmp;
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%lld",&A[i][j]);
        }
    }

    for(int k=0;k<31;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if( ( (1ll<

 

你可能感兴趣的:(矩阵)