FZU 2225 小茗的魔法阵 扫描线+树状数组

这个题和一个CF上的找"Z"的题差不多,都是扫描线+树状数组

从右上角的主对角线开始扫描,一直扫到左下角,每次更新,右延伸等于该扫描线的点,注意在其所在的树状数组更新就好了

时间复杂度O(n^2logn)

#include <stdio.h>
#include <iostream>
#include <vector>
#include <math.h>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <string.h>
#include <string>
using namespace std;
typedef long long LL;
const LL mod=1e8+7;
const int N=1e3+5;
int c[2005][N],n,m;
char s[N][N];
int r[N][N],xl[N][N],xr[N][N];
struct Point
{
    int x,y;
};
vector<Point>g[2005];
void add(int pos,int x)
{
    for(int i=x; i<=m; i+=i&(-i))
        ++c[pos][i];
}
int sum(int pos,int x)
{
    int ans=0;
    if(x==0)return 0;
    for(int i=x; i>0; i-=i&(-i))
        ans+=c[pos][i];
    return ans;
}
int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n+m; ++i)g[i].clear();
        for(int i=1; i<=n; ++i)scanf("%s",s[i]+1);
        memset(xl,0,sizeof(xl));
        memset(xr,0,sizeof(xr));
        memset(r,0,sizeof(r));
        memset(c,0,sizeof(c));
        for(int j=m; j>0; --j)
            for(int i=1; i<=n; ++i)
                if(s[i][j]==‘x‘)r[i][j]=r[i][j+1]+1;
        for(int i=n; i>0; --i)
            for(int j=1; j<=m; ++j)
                if(s[i][j]==‘x‘)xl[i][j]=xl[i+1][j-1]+1,xr[i][j]=xr[i+1][j+1]+1;
        for(int i=1; i<=n; ++i)
            for(int j=1; j<=m; ++j)
                if(s[i][j]==‘x‘)
                {
                    int x=i,y=j+r[i][j]-1;
                    if(x<y)y-=(x-1),x=1;
                    else x-=(y-1),y=1;
                    Point tmp;
                    tmp.x=i,tmp.y=j;
                    int id;
                    if(x==1)id=y;
                    else id=m+x;
                    g[id].push_back(tmp);
                }
        int res=0;
        for(int i=m; i>=1; --i)
        {
            for(int j=0; j<g[i].size(); ++j)
            {
                int pos=g[i][j].x+g[i][j].y;
                add(pos,g[i][j].y);
            }
            for(int x=1,y=i; x<=n&&y<=m; ++x,++y)
            {
                if(s[x][y]!=‘x‘)continue;
                int l=min(xl[x][y],xr[x][y]);
                res+=sum(x+y,y)-sum(x+y,y-l);
            }
        }
        for(int i=2; i<=n; ++i)
        {
            for(int j=0; j<g[m+i].size(); ++j)
            {
                int pos=g[i+m][j].x+g[i+m][j].y;
                add(pos,g[i+m][j].y);
            }
            for(int x=i,y=1; x<=n&&y<=m; ++x,++y)
            {
                if(s[x][y]!=‘x‘)continue;
                int l=min(xl[x][y],xr[x][y]);
                res+=sum(x+y,y)-sum(x+y,y-l);
            }
        }
        printf("Case #%d: %d\n",++cas,res);
    }
    return 0;
}

时间: 2024-08-28 12:38:00

FZU 2225 小茗的魔法阵 扫描线+树状数组的相关文章

[扫描线+树状数组]解决矩形包含点的问题

今天做到第二题,大部分的思路都理解了之后最后剩下一个问题 zzx:“然后扫描线+树状数组搞一下就好了” 看到这两个算法就产生了一种我肯定会的错觉... 然后后来发现并不会的时候很惭愧... 然后十分感谢YDC,在之前完全陌生的情况下 我去问这样一个问题 超级超级超级耐心地给我解答  我问着每一个他们看起来显然的具体处理方法 突然发现真的像张老师说的:“你普及升提高的这条路没有怎么走,中间那块知识是空着的啊” 今天才切实体会到...但是或许正是这样今天学到这样的一个东西更让我觉得开心吧 树状数组解

【BZOJ4009】[HNOI2015]接水果 DFS序+整体二分+扫描线+树状数组

[BZOJ4009][HNOI2015]接水果 Description 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black,  她觉得这个游戏太简单了,于是发明了一个更加难的版本.首先有一个地图,是一棵由 n 个顶点.n-1 条边组成的树(例如图 1给出的树包含 8 个顶点.7 条边).这颗树上有 P 个盘子,每个盘子实际上是一条路径(例如图 1 中顶点 6 到顶点 8 的路径),并且每个盘子还有一个权值.第 i 个盘子

【bzoj4540】[Hnoi2016]序列 单调栈+离线+扫描线+树状数组区间修改

题目描述 给出一个序列,多次询问一个区间的所有子区间最小值之和. 输入 输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数.接下来一行,包含n个整数,以空格隔开,第i个整数为ai,即序列第i个元素的值.接下来q行,每行包含两个整数l和r,代表一次询问. 输出 对于每次询问,输出一行,代表询问的答案. 样例输入 5 5 5 2 4 1 3 1 5 1 3 2 4 3 5 2 5 样例输出 28 17 11 11 17 题解 单调栈+离线+扫描线+树状数组区间修改 首先把使用单调栈找出每个

【BZOJ1818】[Cqoi2010]内部白点 扫描线+树状数组

[BZOJ1818][Cqoi2010]内部白点 Description 无限大正方形网格里有n个黑色的顶点,所有其他顶点都是白色的(网格的顶点即坐标为整数的点,又称整点).每秒钟,所有内部白点同时变黑,直到不存在内部白点为止.你的任务是统计最后网格中的黑点个数. 内部白点的定义:一个白色的整点P(x,y)是内部白点当且仅当P在水平线的左边和右边各至少有一个黑点(即存在x1 < x < x2使得(x1,y)和(x2,y)都是黑点),且在竖直线的上边和下边各至少有一个黑点(即存在y1 <

HDU - 5741 Helter Skelter 扫描线 + 树状数组

HDU - 5741 我们枚举段的起点和终点, 那么每一种情况0的范围是[lx, rx], 1的出现范围是[ly, ry], 可以在二维平面上用矩形表示. 然后问题就变成了询问点有没有被至少一个矩形覆盖, 扫描线 + 树状数组就可以了. #pragma GCC optimize(2) #pragma GCC optimize(3) #include<bits/stdc++.h> #define LL long long #define LD long double #define ull un

HDU 5862 Counting Intersections (离散化+扫描线+树状数组)

题意:给你若干个平行于坐标轴的,长度大于0的线段,且任意两个线段没有公共点,不会重合覆盖.问有多少个交点. 析:题意很明确,可是并不好做,可以先把平行与x轴和y轴的分开,然后把平行y轴的按y坐标从小到大进行排序,然后我们可以枚举每一个平行x轴的线段, 我们可以把平行于x轴的线段当做扫描线,只不过有了一个范围,每次要高效的求出相交的线段数目,可以用一个优先队列来维护平行y轴的线段的上坐标, 如果在该平行于x轴的范围就给相应的横坐标加1,这样就很容易想到是用树状数组来维护,然后每次求出最左边和最右边

hdu 1556 Color the ball (扫描线+树状数组)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1556 Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 14237    Accepted Submission(s): 7120 Problem Description N个气球排成一排,从左到右依次编号为1,

bzoj 1818 [CQOI 2010] 内部白点 - 扫描线 - 树状数组

题目传送门 快速的列车 慢速的列车 题目大意 一个无限大的方格图内有$n$个黑点.问有多少个位置上下左右至少有一个黑点或本来是黑点. 扫描线是显然的. 考虑一下横着的线段,取它两个端点,横坐标小的地方放一个+1,大的地方放一个-1事件. 然后扫描,扫到的横着的线段更新,竖着的线段用树状数组求答案. 然后考虑这一列上原来存在的黑点有没有被统计,如果没有就加上. Code 1 /** 2 * bzoj 3 * Problem#1818 4 * Accepted 5 * Time: 1824ms 6

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

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