天天看点

Educational Codeforces Round 8(E. Zbazi in Zeydabad(树状数组优化))

题目链接:点击打开链接

题意:一个n*m矩阵, 里面的格子除了'z'就是'.',问有多少个z形图案。

思路:因为n和m很大, 即使n^3复杂度也会超时。  如果按照最朴素的方法, 我们可以处理一下前缀和, 处理出一个格子向左l[i][j]、向右r[i][j]、斜向左下lr[i][j]连着的z到哪里为止, 这样我们用n^2复杂度枚举每一个格子作为z形图案的右上角,取min(l[i][j], lr[i][j]), 就可以立刻知道这个z形的最左下角到哪里, 然后在这个对角线上扫一遍, 看看向右最远是不是符合条件,复杂度n^3。  我们注意到, 最后一步是处理一个对角线区间上的问题。  这其实可以用树状数组快速累加和。

我们只需要从右向左枚举每一列, 将以这一列为最右端的线段最左端加进树状数组, 那么我们再枚举这一列的所有点作为Z形的右上角, 算出左下角。  由于只有同一对角线上的行加列相同, 所以以此为树状数组编号, 开n + m棵树状数组, 就可以快速累加在这个对角线上的满足列介于上面那一横的范围内的下面那一横的数量了。 又以为从右向左枚举列, 所以先加的一定满足之后的。

细节参见代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int mod = 1000000000 + 7;
const int INF = 1000000000;
const int maxn = 3000 + 10;
int T,n,m, bit[maxn*2][maxn], l[maxn][maxn],lr[maxn][maxn],r[maxn][maxn];
ll ans = 0;
struct node {
    int x, y;
};
vector<node> g[maxn];
char s[maxn][maxn];
int sum(int i, int x) {
    int ans = 0;
    while(x > 0) {
        ans += bit[i][x];
        x -= x & -x;
    }
    return ans;
}
void add(int i, int x, int d) {
    while(x <= m) {
        bit[i][x] += d;
        x += x & -x;
    }
}
void pre() {
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            if(s[i][j] == 'z') l[i][j] = l[i][j-1]+1;
            else l[i][j] = 0;
        }
        for(int j=m;j>=1;j--) {
            if(s[i][j] == 'z') r[i][j] = r[i][j+1]+1;
            else r[i][j] = 0;
        }
    }
    for(int i=n;i>=1;i--) {
        for(int j=1;j<=m;j++) {
            if(s[i][j] != 'z') { lr[i][j] = 0; continue;}
            if(j-1 >= 1 && i+1 <= n) {
                lr[i][j] = lr[i+1][j-1] + 1;
            }
            else lr[i][j] = 1;
        }
    }
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            g[j + r[i][j] - 1].push_back(node{i, j});
        }
    }
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) {
        scanf("%s",s[i]+1);
    }
    ans = 0;
    pre();
    for(int j=m;j>=1;j--) {
        int len = g[j].size();
        for(int i=0;i<len;i++) {
            int& x = g[j][i].x, &y = g[j][i].y;
            add(x+y, y, 1);
        }
        for(int i=1;i<=n;i++) {
            if(s[i][j] != 'z') continue;
            int len = min(l[i][j], lr[i][j]);
            int y = j - len + 1;
            ans += sum(j+i, j) - sum(i+j, j-len);
        }
    }
    printf("%I64d\n",ans);
    return 0;
}