题目:

图片说明

思路:

这题的思路当然就是枚举每个释放魔法的地方啦,然后检查这片区域有没有建筑,有的话就要停止施法,++ans。假设我们要施法的区域是下面这个红区域,检查红区域内有无建筑就好
图片说明

二维前缀和介绍:

当然如果直接暴力的话复杂度 图片说明 那绝对爆炸的,这里就要引入二维前缀和了,sum[ i ][ j ]表示(0,0)到(i,j)的前缀和。
先给出递推公式 sum[ i ][ j ] = map[ i ][ j ]+sum[ i-1 ][ j ]+sum[ i ][ j-1 ]-sum[ i-1 ][ j-1 ]

图片说明

看图就知道了,sum[i][j] 表示的是蓝区域红区域加黄区域再加右下角那个

sum[i-1][j] 表示的是蓝区域加黄区域,sum[i][j-1] 表示的是蓝区域加红区域
因为蓝区域重复加了所以要减掉他。
然后我们就预处理完了sum数组,接下来就是要求出(i,j)到(i+k,j+k)区域内有无建筑

图片说明
要计算红区域内的建筑数

  • sum[ x2 ][ y2 ] 表示红区域+紫区域+黄区域+蓝区域
  • sum[ x1 ][ y2 ] 表示 黄区域 +紫区域
  • sum[ x2 ][ y1 ] 表示 蓝区域 + 紫区域
  • sum[ x1 ][ y1 ] 表示 紫区域

稍微想想就知道红区域建筑数等于 sum[x2][y2] - sum[x1][y2] - sum[x2][y1] + sum[x1][y1]

因为只要区域内有建筑就会停止施法,所以判断有无建筑就好。
然后枚举左上角的起始点保证不重复,接下来上代码吧

代码:

#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
#pragma GCC optimize(3)
typedef long long ll;
#define INF 0x3f3f3f3f
const int mod = 1e9+7;
const int maxn = 1e5+5;
inline ll read(){
    ll s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}
int sum[1005][1005];
int main(){
    int n = read(),m = read(),k = read();
    string s;
    for(int i = 1 ; i <= n ; ++i){
        cin>>s;
        for(int j = 1 ; j <= m ;++j){
            if(s[j-1]=='1') sum[i][j] = 1;
        }
    }
    for(int i = 1 ; i <= n ;++i){
        for(int j = 1 ; j <= m ;++j){
            sum[i][j] +=(sum[i-1][j] +sum[i][j-1] - sum[i-1][j-1]);
        }
    }
    ll ans = 0;
    for(int i = 1 ; i <= n-k+1 ;++i){
        for(int j = 1 ; j <= m-k+1 ;++j){
            int tmp = sum[i+k-1][j+k-1] - sum[i+k-1][j-1] - sum[i-1][j+k-1] + sum[i-1][j-1];
             if(tmp) ans += 1;
        }
    }
    cout<<ans<<endl;
}