Problem
- 给出n*n由.和#组成的网格,q组询问,每组询问给出起点和终点,求每次将箱子从起点推向终点的最大箱子边长(障碍不能在箱子所占范围内)。n<=1000,q<=300000。4s,512M。
Solution
- 一开始想用floyd预处理,然后O(1)查询,但点的数量级为1e6,会T。
- 由此题可推出的小结论:当最短路问题的距离定义为路径瓶颈时,答案等价于最值生成树上路径的最值。
- 预处理求出以每个点为中心的箱子最大边长,一次bfs或者二维前缀和+二分,bfs时从障碍往外bfs。
- 正解1:kruskal重构树+树上倍增。根据上面的小结论,建立网格中非障碍点的最大生成树,边为两点的最小点权,在此过程中构建kruskal重构树,每次询问两点路径的答案即为重构树上两点的最近公共祖先lca,lca用树上倍增求,总时间复杂度O(qlogn^2)。注意:得到的可能是重构森林。
- kruskal重构树笔记
- 正解2:最大生成树+树链剖分+线段树维护区间最值,或者不用线段树和树链剖分,只涉及查询直接在树上倍增。
- 树上倍增求LCA
- 树链剖分
Code(有点细节)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=1005;
int n,node[maxn*maxn*2],bd[maxn],xx[8]={-1,1,0,0,-1,-1,1,1},yy[8]={0,0,-1,1,1,-1,1,-1},cnt,f[maxn*maxn*2],dep[maxn*maxn*2],log[maxn*maxn],last[maxn*maxn*2];
int logg[maxn*maxn],bz[maxn*maxn*2][23],nd,q,vis[maxn][maxn];
char gra[maxn][maxn];
struct edge{
int u,v,w,next;
}e[maxn*maxn*16];
struct nodee{
int x,y;
};
bool cmp(const edge &a,const edge &b)
{
return a.w>b.w;
}
inline void add(int u,int v,int w)
{
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].next=last[u];
last[u]=cnt;
}
int find(int x)
{
return x==f[x]?x:f[x]=find(f[x]);
}
void ex_kruskal()
{
for(int i=1;i<=n*n*2;i++)
f[i]=i;
sort(e+1,e+1+cnt,cmp);
int cnt0=cnt;
for(int i=1;i<=cnt0;i++)
{
int v=e[i].v,u=e[i].u;
if(find(v)!=find(u))
{
int vv=find(v),uu=find(u);
add(vv,++nd,0);
add(nd,vv,0);
add(uu,nd,0);
add(nd,uu,0);
f[uu]=f[vv]=nd;
node[nd]=e[i].w;
}
}
}
void makelog()
{
logg[1]=0;
for(int i=2;i<=n*n;i++)
logg[i]=logg[i-1]+((1<<logg[i-1]+1)==i);
}
void dfs(int u,int fa)
{
for(int i=1;i<=20;i++) bz[u][i]=bz[bz[u][i-1]][i-1];
for(int i=last[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
dep[v]=dep[u]+1;
bz[v][0]=u;
if(v>n*n)
dfs(v,u);
else{
for(int j=1;j<=20;j++)
bz[v][j]=bz[bz[v][j-1]][j-1];
}
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
while(dep[x]!=dep[y])
x=bz[x][logg[dep[x]-dep[y]]];
if(x==y) return x;
for(int k=logg[dep[x]];k>=0;k--)
if(bz[x][k]!=bz[y][k])
x=bz[x][k],y=bz[y][k];
return bz[x][0];
}
void bfs()
{
int inf=1e8;
queue<nodee>q;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(gra[i][j]=='#')
q.push({i,j});
else
vis[i][j]=inf;
}
}
while(!q.empty())
{
nodee nw=q.front();
q.pop();
for(int i=0;i<8;i++)
{
int nx=nw.x+xx[i],ny=nw.y+yy[i];
if(nx>=1&&nx<=n&&ny>=1&&ny<=n&&gra[nx][ny]!='#'&&vis[nx][ny]==inf)
{
vis[nx][ny]=vis[nw.x][nw.y]+1;
q.push({nx,ny});
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(gra[i][j]!='#')
{
vis[i][j]=2*vis[i][j]-1;
node[(i-1)*n+j]=min(vis[i][j],2*min(min(i,j),min(n-i+1,n-j+1))-1);
}
}
}
}
int main()
{
scanf("%d",&n);
nd=n*n+1;
for(int i=1;i<=n;i++)
scanf("%s",gra[i]+1);
bfs();
for(int i=1;i<=n*n;i++)
{
int x=i/n+1,y=i%n;
if(!y){x--;y=n;}
if(gra[x][y]=='#') continue;
for(int i=0;i<4;i++)
{
int nx=x+xx[i],ny=y+yy[i];
if(nx<=n&&nx>=1&&ny<=n&&ny>=1&&gra[nx][ny]=='.')
e[++cnt]={n*(x-1)+y,n*(nx-1)+ny,min(node[n*(x-1)+y],node[n*(nx-1)+ny])};
}
}
ex_kruskal();
makelog();
for(int i=n*n+1;i<=nd;i++)
{
if(find(i)==i)
{
dep[i]=1;
dfs(i,0);
}
}
cin>>q;
for(int i=1;i<=q;i++)
{
int a1,a2,b1,b2;
scanf("%d%d%d%d",&a1,&a2,&b1,&b2);
cout<<node[lca((a1-1)*n+a2,(b1-1)*n+b2)]<<endl;
}
return 0;
}