【题目】
BZOJ
给定一个
n
×
n
n\times n
n×n的矩阵,矩阵中有空地和障碍两种格子。设集装箱的坐标为其中点坐标,其长度均为奇数,
Q
Q
Q次询问能将最多大的集装箱不触碰障碍的情况下从起点运到终点。
n
≤
1000.
Q
≤
3
×
1
0
5
n\leq 1000.Q\leq 3\times 10^5
n≤1000.Q≤3×105
【解题思路】
首先我们显然要求出以每个点为中心的集装箱最大能是多少。这个问题可以通过预处理出一个点向上下左右能扩展到多少,然后以每一行单位处理出区间往上下最多能扩展多少的
RMQ
\text{RMQ}
RMQ数组,这样每个点可以通过二分答案来做到总
O
(
n
2
log
n
)
O(n^2\log n)
O(n2logn)。当然这个问题可以利用每次往右移动一格,扩展答案至多减少一的性质做到
O
(
n
2
)
O(n^2)
O(n2)。(不过我比较懒就没怎么想)
接下来实际上就是要求两点间任意路径,使得最小值最大。这是一个经典问题,我们可以处理出关于点的最大生成树,接着答案就是路径上最小值。具体来说,我们从大到小枚举每一个点,将其标记为可用,若一个点被标记为可用时,与它相邻的点是可用的且它们不在一个连通块,就将其连起来。处理路径最小值可以使用简单倍增,注意一些细节即可。
复杂度 O ( ( n 2 + Q ) log ( n 2 ) ) O((n^2+Q)\log (n^2)) O((n2+Q)log(n2))
【参考代码】
#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
typedef pair<int,int> pii;
const int N=1003,M=N*N,inf=0x3f3f3f3f;
const int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
int n,Q,fc[25],Log[M];
namespace IO
{
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;
namespace Tree
{
int cnt,tot,head[M],bl[M],len[M],dep[M],f[M];
int fa[22][M],mi[22][M];
struct Tway{int v,nex;}e[M<<1];
void add(int u,int v)
{
e[++tot]=(Tway){v,head[u]};head[u]=tot;
e[++tot]=(Tway){u,head[v]};head[v]=tot;
}
int id(int x,int y){return (x-1)*(n+1)+y;}
//void re(int x){printf("%d %d\n",x/(n+1)+1,x%(n+1));}
void dfs(int x)
{
bl[x]=cnt;mi[0][x]=len[x];
for(int j=1;fc[j]<=dep[x];++j)
fa[j][x]=fa[j-1][fa[j-1][x]],mi[j][x]=min(mi[j-1][x],mi[j-1][fa[j-1][x]]);
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(v==fa[0][x]) continue;
fa[0][v]=x;dep[v]=dep[x]+1;dfs(v);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=0,t=dep[x]-dep[y];i<21;++i)
if(t&fc[i]) x=fa[i][x];
for(int i=20;~i;--i) if(fa[i][x]^fa[i][y])
x=fa[i][x],y=fa[i][y];
return x==y?x:fa[0][x];
}
int jump(int x,int d)//calc except lca
{
//printf("jump:");re(x);
int res=inf;
for(int i=0;i<21;++i) if(d&fc[i])
res=min(res,mi[i][x]),x=fa[i][x];
//puts("jumpend");
return res;
}
int calc(int x,int y)
{
if(bl[x]^bl[y]) return 0;
//printf("%d %d %d %d\n",x/(n+1)+1,x%(n+1),y/(n+1)+1,y%(n+1));
int z=lca(x,y),res=mi[0][z];
//printf("lca:");re(z);
res=min(res,jump(x,dep[x]-dep[z]));
res=min(res,jump(y,dep[y]-dep[z]));
return res;
}
int findf(int x){return f[x]==x?x:f[x]=findf(f[x]);}
}
using namespace Tree;
namespace DreamLolita
{
int cb;
int a[N][N],L[N][N],R[N][N],U[N][N],D[N][N],st[13][N];
bool cuse[N][N];
char s[N];
struct data
{
int l,d;
data(int _l=0,int _d=0):l(_l),d(_d){}
bool operator < (const data&rhs)const{return l>rhs.l;}
}b[M];
void init()
{
n=read();
for(int i=1;i<=n;++i)
{
scanf("%s",s+1);
for(int j=1;j<=n;++j) if(s[j]=='.') a[i][j]=1;
}
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(a[i][j])
{
if(a[i-1][j]) U[i][j]=U[i-1][j]+1;
else U[i][j]=1;
if(a[i][j-1]) L[i][j]=L[i][j-1]+1;
else L[i][j]=1;
}
for(int i=n;i;--i) for(int j=n;j;--j) if(a[i][j])
{
if(a[i+1][j]) D[i][j]=D[i+1][j]+1;
else D[i][j]=1;
if(a[i][j+1]) R[i][j]=R[i][j+1]+1;
else R[i][j]=1;
}
fc[0]=1;for(int i=1;i<23;++i)fc[i]=fc[i-1]<<1;
for(int i=2;i<M;++i) Log[i]=Log[i>>1]+1;
}
void getrmq(int x)
{
for(int i=1;i<=n;++i) st[0][i]=min(U[x][i],D[x][i]);
for(int j=1;j<11;++j) for(int i=1;i+fc[j]-1<=n;++i)
st[j][i]=min(st[j-1][i],st[j-1][i+fc[j-1]]);
}
int query(int l,int r)
{
if(l>r) return 0;
int t=Log[r-l+1];
return min(st[t][l],st[t][r-fc[t]+1]);
}
void getlen()
{
for(int i=1;i<=n;++i)
{
getrmq(i);
for(int j=1;j<=n;++j) if(a[i][j])
{
int l=1,r=min(L[i][j],R[i][j]),res=1;
while(l<=r)
{
int mid=(l+r)>>1;
if(query(j-mid+1,j+mid-1)>=mid) res=mid,l=mid+1;
else r=mid-1;
}
len[id(i,j)]=res*2-1;
}
}
//for(int i=1;i<=n;++i,puts("")) for(int j=1;j<=n;++j) printf("%d ",len[id(i,j)]); puts("");
}
void inittree()
{
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) f[id(i,j)]=id(i,j);
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(a[i][j])
b[++cb]=data(len[id(i,j)],id(i,j));
sort(b+1,b+cb+1);
for(int i=1;i<=cb;++i)
{
int y=b[i].d%(n+1),x=b[i].d/(n+1)+1,now=id(x,y);
cuse[x][y]=1;
for(int d=0;d<4;++d)
{
int nx=x+dx[d],ny=y+dy[d],nid=id(nx,ny);
if(cuse[nx][ny] && findf(now)!=findf(nid))
{
//printf("add:%d %d %d %d\n",x,y,nx,ny);
add(now,nid);
f[findf(now)]=f[findf(nid)];
}
}
}
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
if(a[i][j] && !bl[id(i,j)]) ++cnt,dfs(id(i,j));
//for(int i=1;i<=n;++i,puts("")) for(int j=1;j<=n;++j) printf("%d ",bl[id(i,j)]); puts("");
}
void getans()
{
Q=read();
while(Q--)
{
int xl=read(),yl=read(),xr=read(),yr=read();
writeln(calc(id(xl,yl),id(xr,yr)));
}
}
void solution()
{
init();getlen();
inittree();getans();
}
}
int main()
{
DreamLolita::solution();
return 0;
}