• ABC186F Rook on Grid 树状数组


    atcoder上的题虽然有的很水,但是还是很有点东西的

    即使是难度还不如CFDiv3的Abc,后两道题也是非常有启发性

    这道题就是一个典型的例子

    传送门

    题意:

    在一个h*w的棋盘上,车在(1,1)的位置,棋盘上有m个阻碍,请问车走两步以内能够覆盖的路径有多少个格子

    数据范围:棋盘大小2e5*2e5,障碍物数量2e5

    题解:

    首先想到,车要在两步以内走到尽量多的格子,必然向右走一步,向下走一步,或者先向下,再向右。

    显然,任何障碍物都会堵上自己右方和下方所有格子的其中一条路。

    那么,车走不到的格子,就必然是先右再下,先下再右的路径都被堵上。

    在下图中,浅红色格子就是其中一条路径被堵上了,深红色就是两条都被堵上了

    再考虑是稀疏矩阵,依次遍历,用树状数组维护右下路径被堵住的列,将被右下路径堵住的格子数排除出答案,然后对于每一行,在树状数组上求区间和,算出下右路径能补偿回多少个被堵住的格子。

    慢着,考虑这种情况没有:

    在这种情况下,右上角的三个格子显然无法到达,但是用上面那种方法分析,似乎只被堵住了下右路径,右下路径还是畅通的,但是实际上,右下路径在向右移动时就被第一列的格子堵住了,所以,当有障碍物处于第一行或者第一列的时候,要考虑特殊情况。赛后我想出的方法是,添加虚拟的障碍物。

    像这样,灰色标志虚拟障碍物

     

    或者这样

    但是在添加虚拟障碍物之后,要记得数组范围,并且要去重。

    代码:

    #include<iostream>
    #include<algorithm>
    #define lowbit(a) ((a)&-(a))
    #define LL long long
    using namespace std;
    struct Point{
        int x,y;
        friend bool operator < (const Point &a,const Point &b){
            return a.x==b.x?a.y<b.y:a.x<b.x;
        }
        friend bool operator ==(const Point &a,const Point &b){
            return a.x==b.x && a.y==b.y;
        }
    }p[600005];
    int c[200005];
    int a[200005];
    void update(int x,int y,int n){
        for(int i=x;i<=n;i+=lowbit(i))
            c[i] += y;
    }
    int getsum(int x){
        int ans = 0;
        for(int i=x;i;i-=lowbit(i))
            ans += c[i];
        return ans;
    }
    int main(){
        int h,w,m;
        scanf("%d %d %d",&h,&w,&m);
        int upx=h+1,upy=w+1;
        for(int i=1;i<=m;i++){
            scanf("%d %d",&p[i].x,&p[i].y);
            if(p[i].x==1)upy=min(upy,p[i].y);
            if(p[i].y==1)upx=min(upx,p[i].x);
        }
        for(int i=upx+1;i<=h;i++){
            m++;
            p[m].x=i;p[m].y=1;
        }
        for(int i=upy+1;i<=w;i++){
            m++;
            p[m].y=i;p[m].x=1;
        }
        sort(p+1,p+1+m);
        m=unique(p+1,p+1+m)-p-1;
        int lst=1;
        LL ans=0;
        for(int i=1;i<=m;i++){
            //纵向构造
            if(a[p[i].y]==0){
                a[p[i].y]=1;
                update(p[i].y,1,w);
            }
            if(i==m || p[i+1].x!=p[i].x){
                //横向搜索
                ans+=getsum(w)-getsum(p[lst].y-1);
                lst=i+1;
            }
        }
        printf("%lld
    ",1LL*h*w-ans);
        return 0;
    }
  • 相关阅读:
    c++ CPO ADL
    c++ intrusive
    c++边界检查
    C++仿函数
    C++ RefBase
    c++ vector容器的尺寸问题
    关于调用约定
    C++学习之字符串类、容器
    C++异常
    Git常用命令大全,迅速提升你的Git水平
  • 原文地址:https://www.cnblogs.com/isakovsky/p/14162974.html
Copyright © 2020-2023  润新知