SPOJ_1329 KPMATRIX

http://www.spoj.pl/problems/KPMATRIX/

题意:

给你一个N*M的矩阵,N,M<=250,所有和 >=A && <=B的子矩阵的个数。

思路:

首先我们枚举子矩阵的上下边,复杂度为O(n ^ 2), 然后我们处理出前缀和sum[x] , 当我们在找以x为子矩阵的右边界的子矩阵的时候,就是要找有多少个i,使得 A<= sum[x] - sum[i] <=B ,也就是 sum[x] - B <= sum[i] <= sum[x] - A ,也就是说在某个位置x,我们只要找到它前面一个有多少个i,满足sum[x] - B <= sum[i] <= sum[x] - A ,这个查找是可以在O( logm )的时间内找到的(用一个数状数组维护即可),但是因为 -10^9 <= sum[i] <=10^9 ,所以我们还需要对所有的数进行离散化处理。这样本题的总时间复杂度就是:O(n^2*(m + m*logm) ) 。

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>

const int NN = 260 ;
int N,M, A, B;
int map[NN][NN] ;
int sum[NN] ;
int sumB[NN] , sumA[NN] ;
int xx[NN*3] ;
int C[3 * NN] ;


int find( int val , int l ,int r ){
    while( l<=r ){
        int mid = (l + r) >> 1  ;
        if( xx[mid] == val )    return mid ;
        if( xx[mid] < val ) l = mid + 1 ;
        else            r = mid - 1 ;
    }
    return -1 ;
}

inline int lowbit(int a){ return a&(-a)  ; }

void add(int a , int n){
        while( a<=n ){
            C[a] ++ ; a += lowbit(a) ;
        }
}

int cal_sum(int n){
    int res = 0 ;
    while( n>=1 ){
        res += C[n]  ;
        n -= lowbit(n) ;
    }
    return res ;
}
void solve(){
    int ans = 0 ;
    for(int i=1;i<=N;i++){
        for(int j=i;j<=N;j++){
            int cnt = 1 ;
            sum[0] = 0  ;
            sumA[0] = sum[0] - A ;
            sumB[0] = sum[0] - B - 1 ;
            xx[cnt++ ] = sum[0]  ;
            xx[cnt++ ] = sumA[0] ;
            xx[cnt++ ] = sumB[0] ;

            for(int k=1;k<=M;k++){
                sum[k] = sum[k-1] + map[j][k] - map[i-1][k] ;
                sumA[k] = sum[k] - A ;
                sumB[k] = sum[k] - B - 1;
                xx[cnt ++] = sum[k]  ;
                xx[cnt ++] = sumB[k] ;
                xx[cnt ++] = sumA[k] ;
            }
            std::sort( xx + 1 , xx + cnt ) ;
            int tot = 2 ;
            for(int i=2;i<cnt;i++){
                if( xx[i] == xx[i-1] )  continue ;
                xx[ tot++ ] = xx[i] ;
            }
            tot -- ;
            memset( C , 0 ,sizeof(C) );
            add( find( sum[0] , 1 , tot ) , tot ) ;
            for(int i=1;i<=M;i++){
                int e = find( sumA[i] , 1 , tot ) ;
                int s = find( sumB[i] , 1 , tot ) ;
                ans += cal_sum(e) - cal_sum(s) ;
                add( find( sum[i] ,1 , tot) , tot ) ;
            }
        }
    }
    printf("%d\n",ans);
}

int main(){
    while( scanf("%d %d",&N,&M) == 2 ){
        for(int j=1;j<=M;j++)   map[0][j] = 0 ;
        for(int i=1;i<=N;i++){
            for(int j=1;j<=M;j++){
                scanf("%d",&map[i][j]) ;
                map[i][j] = map[i-1][j] + map[i][j] ;
            }
        }
        scanf("%d %d",&A,&B);
        solve() ;
    }
    return 0 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值