题意
很好懂的题意,给一个有障碍物的网格图,求起点到终点路径的最大正方形大小。
意思就是,以每个点为中心,奇数边长的正方形区域内不能有障碍点。
起点到终点多个询问
题解
预处理
首先肯定要预处理出,每个点的最大正方形大小:用
b
f
s
bfs
bfs实现。
因为我们可以发现,对于每个
2
∗
L
−
1
2*L-1
2∗L−1的正方形,如果从上下左右,对角方向往中心走,边界外一层的点最短路径都是
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)]);
}
}