题意
题目链接
给出一张$n \times m$的网格,其中$1$为蓝点,$2$为白点。
$Q$次询问,每次询问一个子矩阵内蓝点形成的联通块的数量
保证任意联通块内的任意蓝点之间均只有一条路径可达
Sol
mdzz不好好读题目还想做题,。。
题目中说“联通块内的任意点都只有一条路径可达”,不难推断出这是一棵树
因此 联通块个数 = 蓝点的数量 - 蓝点间边的数量
考虑用前缀和维护,点的数量好处理,但是这个边的数量有点麻烦
反正我用一个数组是搞不出来,因为无法判断左右的方向。。
那就行列分别记录一下就可以了。
#include<cstdio>
#include<iostream>
#include<cstring>
#define LL long long
using namespace std;
const int MAXN = 2001;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int N, M, Q;
char s[MAXN][MAXN];
int P[MAXN][MAXN], R[MAXN][MAXN], L[MAXN][MAXN];
int GetP(int x, int y) {
if(x == 0 || y == 0) return 0;
return P[x - 1][y] + P[x][y - 1] - P[x - 1][y - 1];
}
int GetR(int x, int y) {
if(x == 0 || y == 0) return 0;
return R[x - 1][y] + R[x][y - 1] - R[x - 1][y - 1];
}
int GetL(int x, int y) {
if(x == 0 || y == 0) return 0;
return L[x - 1][y] + L[x][y - 1] - L[x - 1][y - 1];
}
main() {
N = read(); M = read(); Q = read();
for(int i = 1; i <= N; i++) {
scanf("%s", s[i] + 1);
for(int j = 1; j <= M; j++) {
P[i][j] = GetP(i, j);
R[i][j] = GetR(i, j);
L[i][j] = GetL(i, j);
if(s[i][j] == '1') L[i][j] += (s[i - 1][j] == '1'),
R[i][j] += (s[i][j - 1] == '1'),
P[i][j]++;
}
}
/*for(int i = 1; i <= N; i++, puts(""))
for(int j = 1; j <= M; j++)
printf("%d ", L[i][j]);*/
while(Q--) {
int x1 = read(), y1 = read(), x2 = read(), y2 = read();
// printf("%d %d %d %d\n", GetP(x2, y2), GetP(x1 - 1, y2), GetP(x2, y1 - 1), GetP(x1 - 1, y1 - 1));
int ans1 = P[x2][y2] - P[x1 - 1][y2] - P[x2][y1 - 1] + P[x1 - 1][y1 - 1];
int ans2 = R[x2][y2] - R[x1 - 1][y2] - R[x2][y1] + R[x1 - 1][y1];
int ans3 = L[x2][y2] - L[x2][y1 - 1] - L[x1][y2] + L[x1][y1 - 1];
cout << ans1 - ans2 - ans3 << endl;
}
return 0;
}
/*
*/
作者:自为风月马前卒
个人博客http://attack204.com//
出处:http://zwfymqz.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。