LG3684 [CERC2016]机棚障碍 Hangar Hurdles

时间:2023-03-10 00:27:29
LG3684 [CERC2016]机棚障碍 Hangar Hurdles

题意

题目描述

你正在评估一些关于一个巨型飞机仓库的建设计划。飞机仓库的地面可以表示为n行n列的网格图,其中每个格子要么是空的,要么有障碍物。行从上到下依次被编号为1到n,列从左到右依次被编号为1到n。

存放飞机零件的大型集装箱能在飞机仓库的地面上*移动是很重要的。我们可以将每个集装箱看作一个以某个格子为中心的边平行于坐标轴的正方形。对于一个奇数k,一个尺寸为k的集装箱是一个包含k行k列的正方形。一个集装箱的坐标为其中心格子的坐标。集装箱可以向上下左右移动,但不能碰到障碍物,且不能移出仓库的边界。

给定q对格子A_k和B_k,对于每对格子,请找到能从A_k移动到B_k的集装箱的最大尺寸,注意这个尺寸也要是一个奇数。

输入输出格式

输入格式:

第一行包含一个正整数n(2<=n<=1000),表示仓库的尺寸。

接下来n行,每行n个字符,描述整个仓库,其中“.”表示空格子,“#”表示障碍物。

接下来一行包含一个正整数q(1<=q<=300000),表示询问的个数。

接下来q行,每行4个正整数r_A,c_A,r_B,c_B(1<=r_A,c_A,r_B,c_B<=n),分别表示A和B的坐标。

输入数据保证A和B是不同的空格子。

输出格式:

输出q行,每行一个整数,对于每个询问输出最大尺寸,如果不存在解,那么输出0。

输入输出样例

输入样例#1:
复制
7
.....#.
...#.#.
....#..
....###
....#..
#......
.......
5
2 5 5 2
2 5 3 6
2 2 6 3
2 2 6 6
1 1 7 7
输出样例#1:
复制
1
0
3
1
1

分析

正解就是从每一个障碍出发做八连通bfs,得到每个位置能放多大尺寸的物件(这就是点权),然后从\((a,b)\)到\((c,d)\)的一条路上,经过的点权最小点的点权,就是走这条路径的答案。

所以把每个格子看做一个点,一个格子和其上下左右四个格子连边,边权为这两个格子中较小的点权。做出最大生成树后,查询路径上最小值即可。

然后做法就很多了,我用的是kruskal重构树。

这个算法还有个名字,叫做最小瓶颈生成树

时间复杂度\(O(n^2 \log n+q \log n)\)

代码

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll; co int N=1001,M=2e6+1;
int n,Q,SZ,m;
char mp[N][N];int QAQ[N][N],id[N][N],qx[M],qy[M];
int v[M],ls[M],rs[M],f[M],dep[M],fa[M][21];
struct edge{int x,y,z;}e[M*2];
co int mvx[8]={1,-1,0,0,1,-1,-1,1},mvy[8]={0,0,1,-1,1,-1,1,-1};
bool cmp(co edge&A,co edge&B) {return A.z>B.z;} void bfs(){
int he=1,ta=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(mp[i][j]=='#') ++ta,qx[ta]=i,qy[ta]=j;
else id[i][j]=++SZ,f[SZ]=SZ;
for(int i=1;i<=n;++i) ++ta,qx[ta]=i,qy[ta]=0;
for(int i=1;i<=n;++i) ++ta,qx[ta]=i,qy[ta]=n+1;
for(int i=1;i<=n;++i) ++ta,qx[ta]=0,qy[ta]=i;
for(int i=1;i<=n;++i) ++ta,qx[ta]=n+1,qy[ta]=i;
while(he<=ta){
int x=qx[he],y=qy[he];++he;
for(int i=0;i<8;++i){
int tx=x+mvx[i],ty=y+mvy[i];
if(1<=tx&&tx<=n&&1<=ty&&ty<=n&&mp[tx][ty]!='#'&&!QAQ[tx][ty])
QAQ[tx][ty]=QAQ[x][y]+1,++ta,qx[ta]=tx,qy[ta]=ty;
}
}
} int findf(int x) {return x==f[x]?x:f[x]=findf(f[x]);}
void kruscal(){
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j){
if(mp[i][j]=='#') continue;
if(i!=1&&mp[i-1][j]!='#')
e[++m]=(edge){id[i][j],id[i-1][j],std::min(QAQ[i][j],QAQ[i-1][j])-1};
if(j!=1&&mp[i][j-1]!='#')
e[++m]=(edge){id[i][j],id[i][j-1],std::min(QAQ[i][j],QAQ[i][j-1])-1};
}
std::sort(e+1,e+1+m,cmp);
for(int i=1;i<=m;++i){
int r1=findf(e[i].x),r2=findf(e[i].y);
if(r1!=r2){
++SZ,v[SZ]=e[i].z,ls[SZ]=r1,rs[SZ]=r2;
f[SZ]=f[r1]=f[r2]=SZ;
}
}
}
void dfs(int x){
for(int i=1;i<=20;++i) fa[x][i]=fa[fa[x][i-1]][i-1];
if(ls[x]) fa[ls[x]][0]=x,dep[ls[x]]=dep[x]+1,dfs(ls[x]);
if(rs[x]) fa[rs[x]][0]=x,dep[rs[x]]=dep[x]+1,dfs(rs[x]);
}
int lca(int x,int y){
if(dep[x]<dep[y]) std::swap(x,y);
for(int i=20;i>=0;--i) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=20;i>=0;--i)
if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main(){
// freopen(".in","r",stdin),freopen(".out","w",stdout);
read(n);
for(int i=1;i<=n;++i) scanf("%s",mp[i]+1);
bfs(),kruscal();
for(int i=1;i<=SZ;++i) if(findf(i)==i) dep[i]=1,dfs(i);
for(read(Q);Q--;){
int ax,ay,bx,by;
read(ax),read(ay),read(bx),read(by);
int k=lca(id[ax][ay],id[bx][by]);
if(!k) puts("0");
else printf("%d\n",2*v[k]+1);
}
return 0;
}