"Z"型矩阵
题意
对一个只包含 . . . 和 z z z 的矩阵,当以下条件满足时 :
- 该矩阵的行数列数相等。
- 该矩阵的第一行与最后一行的字符全是 z z z
- 该矩阵从右上角到左下角的对角线上的字符全是 z z z
我们称其为 z z z 矩阵 现在给定一个 n ∗ m n * m n∗m 的矩阵,请你计算它有多少个子矩阵是 z z z 矩阵
n , m n,m n,m 分别表示矩阵的行数和列数
1 ≤ \leq ≤ n , m n,m n,m ≤ \leq ≤ 3 e 3 3e^3 3e3
思路
首先我们可以通过前缀和的思想,先预处理出来左边,右边和斜着的数量。
有了这些之后,发现给的数据范围很大,复杂度必须要在 n l o g n nlogn nlogn 才能过,那现在的暴力写法是 n 3 n^3 n3 的,先 O ( n 2 ) O(n^2) O(n2) 枚举 Z Z Z 型的右上角的那个点,然后对左边和斜下角取 m i n min min 操作,然后再枚举斜下角右边的点能不能满足要求。
但这个写法时间复杂度显然是不行的
这里用了树状数组去优化,以每一条斜下角作为一棵树,可以发现同一颗树下面的点 x + y x+y x+y 下标值之和都是一样的,然后就是考虑每个点插入的优先级。
可以发现每个点的优先级是他自己的下标 y y y 值 加上右边 z z z 值的和,枚举的时候从右向左进行枚举
最后树状数组直接查询那段区间和就好了
代码
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <map>
#include <vector>
#include <queue>
using namespace std;
#define fr first
#define se second
#define int long long
const int maxn = 3e3 + 10;
int n, m;
char a[maxn][maxn];
int pre[maxn][maxn], lst[maxn][maxn];
int xie[maxn][maxn];
int lz[maxn][maxn];
vector<pair<int, int>>g[maxn];
bool in(int x, int y) {
if (x >= 1 && x <= n && y >= 1 && y <= m) return 1;
else return 0;
}
int tree[maxn << 1][maxn];
int ask(int k, int x){
int ans = 0;
for (; x; x -= x & -x)ans += tree[k][x];
return ans;
}
void add(int k, int x, int val){
for (; x <= m; x += x & -x)tree[k][x] += val;
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++) cin >> a[i][j];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] == 'z') pre[i][j] = pre[i][j - 1] + 1;
else pre[i][j] = 0;
}
for (int j = m; j >= 1; j--) {
if (a[i][j] == 'z')lst[i][j] = lst[i][j + 1] + 1;
else lst[i][j] = 0;
}
for (int j = 1; j <= m; j++) {
if (a[i][j] == 'z') g[j + lst[i][j] - 1].push_back(make_pair(i, j));
}
}
for (int i = n; i >= 1; i--) {
for (int j = 1; j <= m; j++) {
if (a[i][j] == 'z') lz[i][j] = lz[i + 1][j - 1] + 1;
else lz[i][j] = 0;
}
}
int ans = 0;
for (int j = m; j >= 1; j--) {
for (int i = 0; i < g[j].size(); i++) {
add(g[j][i].fr + g[j][i].se, g[j][i].se, 1);//插的是点的y值的下标
}
for (int i = 1; i <= n; i++) {
if (a[i][j] == 'z') {
int c = min(pre[i][j], lz[i][j]);
ans += ask(i + j, j) - ask(i + j, j - c);
}
}
}
cout << ans << endl;
}
signed main() {
int t; t = 1;
while (t--) solve();
return 0;
}