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;
}
时间: 2024-10-07 23:52:31

Educational Codeforces Round 8(E. Zbazi in Zeydabad(树状数组优化))的相关文章

CF Educational Codeforces Round 10 D. Nested Segments 离散化+树状数组

题目链接:http://codeforces.com/problemset/problem/652/D 大意:给若干个线段,保证线段端点不重合,问每个线段内部包含了多少个线段. 方法是对所有线段的端点值离散化,按照左端点从大到小排序,顺着这个顺序处理所有线段,那么满足在它内部的线段一定是之前已经扫到过的.用树状数组判断有多少是在右端点范围内. 1 #include <iostream> 2 #include <vector> 3 #include <algorithm>

Codeforces 628E Zbazi in Zeydabad 树状数组

题意:一个n*m的矩阵,要么是 . 要么是 z ,问可以形成几个大z 分析:(直接奉上官方题解,我感觉说的实在是太好了) Let's precalculate the values zlij, zrij, zldij — the maximal number of letters 'z' to the left, to the right and to the left-down from the position (i, j). It's easy to do in O(nm) time. L

codeforces round 512 F. Putting Boxes Together 树状数组维护区间加权平均数

F. Putting Boxes Together time limit per test 2.5 seconds memory limit per test 256 megabytes input standard input output standard output There is an infinite line consisting of cells. There are nn boxes in some cells of this line. The ii-th box stan

Codeforces 220B - Little Elephant and Array 离线树状数组

This problem can be solve in simpler O(NsqrtN) solution, but I will describe O(NlogN) one. We will solve this problem in offline. For each x (0?≤?x?<?n) we should keep all the queries that end in x. Iterate that x from 0 to n?-?1. Also we need to kee

Codeforces 459D Pashmak and Parmida&#39;s problem(树状数组)

题目链接:Codeforces 459D Pashmak and Parmida's problem 题目大意:给定一个序列,定义f(l,r,x)为l≤k≤r并且ak=x的k的个数,求1≤i<j≤n的情况下,f(1,i,ai)<f(j,n,aj)的个数. 解题思路:预处理出f(1,i,ai),f(j,n,aj)值,然后用树状数组维护个数. #include <cstdio> #include <cstring> #include <algorithm> us

Educational Codeforces Round 22 E. Army Creation(主席树)

题目链接:Educational Codeforces Round 22 E. Army Creation 题意: 给你n个数和一个数k,然后有q个询问. 每个询问 有一个区间[l,r],问你这个区间内在满足每一种数不超过k的情况下,最大能选多少个数出来. 强制在线. 题解: 一看就要用到主席树,和主席数求区间内有多少不同的数的个数处理方法相同. 依次将每个数插入,当这个数出现的个数等于k了,就把最前面的那个数删掉. 然后询问就访问root[r]就行了. 第一次写完数据结构没有调试一遍过样例,一

CodeForces 380C Sereja and Brackets(扫描线+树状数组)

[题目链接] http://codeforces.com/problemset/problem/380/C [题目大意] 给出一个括号序列,求区间内左右括号匹配的个数. [题解] 我们发现对于每个右括号,其匹配的左括号是固定的, 我们保存每个右括号匹配的左括号位置, 对区间询问进行线扫描,将扫描的区间右端点及其之前所有的右括号对应的左括号位置做标记, 只要查询询问区间的标记个数就是答案,这个可以用树状数组维护. [代码] #include <cstdio> #include <algor

Testing Round #12 A,B,C 讨论,贪心,树状数组优化dp

题目链接:http://codeforces.com/contest/597 A. Divisibility time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Find the number of k-divisible numbers on the segment [a, b]. In other words you need

Educational Codeforces Round 22 E. Army Creation 主席树 或 分块

E. Army Creation As you might remember from our previous rounds, Vova really likes computer games. Now he is playing a strategy game known as Rage of Empires. In the game Vova can hire n different warriors; ith warrior has the type ai. Vova wants to