zoukankan      html  css  js  c++  java
  • 01矩阵前缀和

    题目描述

    给定一个 (n*m)的 01 矩阵, 求包含 ([l,r])个 1的子矩形个数。

    输入格式

    第一行,两个正整数 n,m。 接下来 n行每一个长度为 m的 01 串,表示给定的矩阵。 接下来 一行, 两个自然数 l,r。

    Solution

    首先暴力枚举显然不可取

    注意到,n非常小,可以考虑一种(O(n^2m))的方法

    很套路,要将询问拆成([0,L-1])([0,R])来处理

    (O(n^2))来枚举矩形的上边和下边,

    而前缀和又是单调的,用个指针维护就好了,

    具体的:如果我们已经确定了(i)列到(j)列间的1的个数在([l,r])中,k列到j列的1也在([l,r])中,并且满足第(i)列比第(k)列小,那么,矩形的一边为(j)的情况下,另一边可以为第(i)列到第(k)列间的任意一列。
    所以,我们就可以通过寻找边界的情况快速找到矩形的右边固定情况所产生的答案。然后将矩形的右边向右移动,判断矩形的左边的范围是否变动,然后将这个范围继续加入答案,就可以解出答案了。

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #define re register int
    using namespace std;
    char s[50010];
    long long n,m,i,j,p[50010],sum[40][50010],q[50010],ans,k,l,r;
    int main(){
    	scanf("%lld%lld",&n,&m);
    	for(int i=1;i<=n;i++){
    		scanf("%s",s+1);
    		for(int j=1;j<=m;j++)
    			sum[i][j]=sum[i-1][j]+s[j]-'0';
    	}
    	scanf("%lld%lld",&l,&r);
    	for(int i=1;i<=n;i++)//上
    		for(int j=i;j<=n;j++){//下
    			long long x=0,y=0;
    			for(int k=1;k<=m;k++){
    				p[k]=sum[j][k]-sum[i-1][k];
    				q[k]=q[k-1]+p[k];
    			}
    			for(int k=1;k<=m;k++){
    				while(q[k]-q[x]>=l&&x<k) x++;
    				while(q[k]-q[y]>r&&y<k) y++;
    				ans+=x-y;
    			}
    		}
    	printf("%lld
    ",ans);
    	return 0;
    } 
    
  • 相关阅读:
    eslint 入门项目搭建过程
    ES6 模块化笔记
    闭包
    JavaScript 内存相关知识
    Mac 配置Charles,抓取移动设备数据
    jquery.cookie的path坑
    如何模拟click事件,打开一个a标签链接?
    6月份开发问题整理
    js 淡入淡出的tab选项卡
    点击弹出模态框-以登录表单为例
  • 原文地址:https://www.cnblogs.com/ke-xin/p/13551854.html
Copyright © 2011-2022 走看看