题目“Z”型矩阵
这题真是阴间啊,听dls听半天不知道怎么维护这个信息,现在大概记录一下我想清楚这个题的心路历程
考虑最暴力的方法,对于每个左上角 ( X , Y ) (X,Y) (X,Y)计算一个Z形矩阵,那么可以再枚举一下Z的右下角那个点,可以发现这个做法是 O ( n 3 ) O(n^3) O(n3)的。需要用树状数组优化一下。
对于每个左上角 ( X , Y ) (X,Y) (X,Y),考虑其向左以及向左下角延伸的最大长度,记为L,那么可以发现,我们需要数的点都会落在 y i ∈ [ y , y − l + 1 ] y_i\in[y, y-l+1] yi∈[y,y−l+1]这根斜线上。所以,对于每一根斜线,都需要建立一个树状数组。再发现每根斜线的 x + y x+y x+y都是确定的,所以可以开一个二维数组来维护。
现在每左上角顶点需要查询的区间以及树状数组都确定了,那么要怎么插入呢?
记点 ( i , j ) (i,j) (i,j)向右延伸的z长度为 r r r, 那么我们需要统计的是 j + r − 1 > = y j+r-1>=y j+r−1>=y那些点,所以我们可以从大到小插入,如果我们想计算点 ( x , y ) (x,y) (x,y),那么我们必须先插入那些 j + r − 1 > = y j+r-1>=y j+r−1>=y的点,所以可以先从最右边的点开始计算。
代码
#include <bits/stdc++.h>
#define x first
#define y second
#define mp make_tuple
using namespace std;
const int N = 3e3 + 10, M = 1e6 + 10, mod = 1e9 + 7, INF = 1e9 + 10;
typedef long long LL;
typedef pair<int,int> PII;
using tp = tuple<int,int,int> ;
bool multi = false;
char s[N][N];
int n, m;
int lst[N][N], rst[N][N], dia[N][N];
struct Fenwick {
int c[N], n;
inline void Init(int _n) {
n = _n;
for (int i = 1; i <= n; ++ i) c[i] = 0;
}
inline void Add(int x, int v) {
for (; x <= n; x += x & -x) c[x] += v;
}
inline int Ask(int x) {
int sm = 0;
while(x) {
sm += c[x];
x &= x - 1;
}
return sm;
}
inline int Sum(int l, int r) {
return Ask(r) - Ask(l - 1);
}
} bit[N << 1];
//保存可以ri, 可以延伸最右边的点(i,j)
vector<tp> rid;
void solve() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%s", s[i] + 1);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++)
if(s[i][j] == 'z') lst[i][j] = lst[i][j - 1] + 1;
else lst[i][j] = 0;
for(int j = m; j; j--)
if(s[i][j] == 'z') rst[i][j] = rst[i][j + 1] + 1;
else rst[i][j] = 0;
for(int j = 1; j <= m; j++) {
if(s[i][j] == 'z') {
rid.push_back({j + rst[i][j] - 1, i, j});
}
}
}
for(int i = n; i; i--) {
for(int j = 1; j <= m; j++)
if(s[i][j] == 'z') dia[i][j] = dia[i + 1][j - 1] + 1;
else dia[i][j] = 0;
}
for(int i = 1; i <= n + m; i++) bit[i].Init(m);
sort(rid.begin(), rid.end());
int k = rid.size() - 1;
LL res = 0;
//从最右边的点开始插入
for(int j = m; j; j--) {
while(k >= 0 && get<0>(rid[k]) >= j) {
auto[rr, x, y] = rid[k--];
bit[x + y].Add(y, 1); //插入y或者x都行,查询稍微不一样
}
for(int i = 1; i <= n; i++) {
if(s[i][j] != 'z') continue;
int L = min(lst[i][j], dia[i][j]);
res += bit[i + j].Sum(j - L + 1, j);
}
}
cout << res << endl;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("C.txt", "r", stdin);
#endif
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T = 1;
if(multi) cin >> T;
while (T--) solve();
return 0;
}