H - Hangar Hurdles

题意

很好懂的题意,给一个有障碍物的网格图,求起点到终点路径的最大正方形大小。
意思就是,以每个点为中心,奇数边长的正方形区域内不能有障碍点。
起点到终点多个询问

题解

预处理

首先肯定要预处理出,每个点的最大正方形大小:用 b f s bfs bfs实现。
因为我们可以发现,对于每个 2 ∗ L − 1 2*L-1 2L1的正方形,如果从上下左右,对角方向往中心走,边界外一层的点最短路径都是 L L L
利用这个性质我们可以进行bfs,不过需要判断网格图边界的影响。

性质

首先 x x x走到 y y y,答案必然小于等于 min ⁡ ( v a l [ x ] , v a l [ y ] ) \min(val[x],val[y]) min(val[x],val[y]),而中间的可以大也可以小,如果大的话不影响,小的话答案就是最小的那一个。
所以我们可以从大的往小的连,表示不受影响的点,如果某个更小的点和我当前已经连通的区域是相邻的,那这个区域最小的点可以直接连向这个新点,因为这个区域里的我最小的是可以随便走的,不会对我的大小产生影响。

最后可以得到多棵树,求lca就是答案,因为如果是一条链,显然lca是答案,如果不是,那么考虑两条链,只有当有一个中间点(更小)连接了这两个集合的点,才会变成一个集合,而且必须要通过这个更小的点,所以这两个集合的最小都指向这个新点,这个新点也是lca

简单地说,对于起点开始到终点,我只关心比我自己小的,对于每个点,他只关心比自己小的点怎么走,比自己大的点怎么走都没关系,从大到小枚举点,对于每个连通块,如果有更小的点相邻,那么集合当前最小点就连向这个点,更大的点会连到当前最小点再到新点,贡献是一样的,这样处理能保证没有环,并且复杂度也是正确的。[相当于并查集不路径压缩的一棵树,不过我代码压缩了,重新建了图]

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e6 + 500;

int n,vis[maxn],f[maxn],cnt;
int dep[maxn],fa[maxn],sz[maxn],son[maxn];
int dfn[maxn],top[maxn],val[maxn];
int dx[8]={0,0,1,-1,1,1,-1,-1};
int dy[8]={1,-1,0,0,1,-1,1,-1};
vector<int>G[maxn],iter[maxn];

int find(int x){return f[x]==x?x:(f[x]=find(f[x]));}
int idx(int x,int y){return (x-1)*n+y;}

void dfs1(int u,int pre){
    dep[u]=dep[pre]+1;
    sz[u]=1,fa[u]=pre;
    int s=0;
    for(auto v:G[u]){
        if(v==pre)continue;
        dfs1(v,u);
        sz[u]+=sz[v];
        if(sz[v]>s)son[u]=v,s=sz[v];
    }
}

void dfs2(int u,int tr_top){
    dfn[u]=++cnt;
    top[u]=tr_top;
    if(son[u])dfs2(son[u],tr_top);
    for(auto v:G[u]){
        if(v==fa[u]||v==son[u])continue;
        dfs2(v,v);
    }
}

int lca(int l,int r){
    while(top[l]!=top[r]){
        if(dep[top[l]]<dep[top[r]])swap(l,r);
        l=fa[top[l]];
    }
    if(dfn[l]<=dfn[r])return l;
    else return r;
}

void bfs(){
//其实我们可以发现一个性质就是奇数长度的矩形,边界上的障碍物从八个方向走最快都是len;
//然后在考虑没有障碍物的情况即可
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)val[idx(i,j)]=1<<30;
    queue<pair<int,int> >q;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(vis[idx(i,j)])q.push({i,j}),val[idx(i,j)]=0;
        }
    }
    while(!q.empty()){
        int x0=q.front().first,y0=q.front().second;q.pop();
        for(int i=0;i<8;i++){
            int x=x0+dx[i],y=y0+dy[i];
            if(x<1||x>n||y<1||y>n)continue;
            if(vis[idx(x,y)]||val[idx(x,y)]!=(1<<30))continue;//首先不能是障碍点、其次不能是已经更新的点
            val[idx(x,y)]=val[idx(x0,y0)]+1;
            q.push({x,y});
        }
    }
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(val[idx(i,j)])val[idx(i,j)]=2*val[idx(i,j)]-1;
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)val[idx(i,j)]=min(val[idx(i,j)],2*min(min(i,j),min(n-i+1,n-j+1))-1);
}

void build(){
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)iter[val[idx(i,j)]].push_back(idx(i,j));
    for(int i=1;i<=n*n;i++)f[i]=i;
    for(int i=1;i<=n;i++){
        //for(int j=1;j<=n;j++)cout<<val[idx(i,j)]<<" ";puts("");
    }
    for(int i=n;i>=1;i--){
        for(int j=0,w;j<iter[i].size();j++){
            w=iter[i][j];
            int x=(w+n-1)/n,y=w-(x-1)*n;
       //     cout<<x<<" "<<y<<" "<<w<<endl;
            for(int k=0;k<4;k++){
                int xx=x+dx[k],yy=y+dy[k];
                if(xx<1||xx>n||yy<1||yy>n||vis[idx(xx,yy)])continue;
                if(val[idx(xx,yy)]<val[idx(x,y)])continue;
                int l=find(idx(x,y)),r=find(idx(xx,yy));
                if(l==r)continue;
                f[r]=l;
                G[l].push_back(r);
                G[r].push_back(l);
                int a,b;
                a=(l+n-1)/n,b=l-(a-1)*n;
              //  cout<<"("<<a<<","<<b<<")"<<"=>";
                a=(r+n-1)/n,b=r-(a-1)*n;
              //  cout<<"("<<a<<","<<b<<")"<<" "<<endl;
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int w=find(idx(i,j));
            int x=(w+n-1)/n,y=w-(x-1)*n;
          //  cout<<" "<<x<<","<<y<<") ";
        }
        //puts("");
    }
    vector<int>vec;
    for(int i=1;i<=n*n;i++)vec.push_back(find(i));
    sort(vec.begin(),vec.end());
    vec.erase(unique(vec.begin(),vec.end()),vec.end());
    //for(auto x:vec)cout<<x<<" ";puts("");
    for(int i=0;i<vec.size();i++){
        dfs1(vec[i],-1);
        dfs2(vec[i],vec[i]);
    }
//    for(int i=1;i<=n*n;i++)cout<<val[i]<<";"<<dep[i]<<endl;puts("");
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        string str;cin>>str;
        for(int j=1;j<=n;j++){
            if(str[j-1]=='#')vis[idx(i,j)]=1;
            else vis[idx(i,j)]=0;
        }
    }
    bfs();
    build();
    int q;cin>>q;
    while(q--){
        int x1,y1,x2,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        int s=idx(x1,y1),t=idx(x2,y2);
        if(find(s)!=find(t)){
            puts("0");
            continue;
        }
        //cout<<dep[s]<<" "<<dep[t]<<endl;
        printf("%d\n",val[lca(s,t)]);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值