P3684 [CERC2016] 机棚障碍 Hangar Hurdles
拓展
讲题之前先讲个扩展,瓶颈生成树
定义
无向图 \(G\) 的瓶颈生成树是这样的一个生成树,它的最大的边权值在 \(G\) 的所有生成树中最小。
性质
最小生成树是瓶颈生成树的充分不必要条件。
可以用反证法证明:
设最小生成树的最大边权为 \(w\) ,如果最小生成树不是瓶颈生成树的话,那么瓶颈生成树中所有的边一定都小于 $w $,把 \(w\) 这条边删掉,然后加上瓶颈生成树中一条边把他连起来,得到的生成树一定小于最小生成树,矛盾
瓶颈生成树不一定是最小生成树,假设 \(w\) 为瓶颈,那么我们可以去掉最小生成树中小于 \(w\) 的边,加上大于这条边并且小于等于 \(w\) 的边让其变成一颗瓶颈生成树,并且不是最小生成树
用法:
求两点之间路径的最大值最小时,可以用到瓶颈生成树
因为最小生成树一定是瓶颈生成树,所以直接求出来最小生成树之后倍增就好了
要是求两点的最小值最大,那就用最大生成树即可
题目描述:
你正在评估一些关于一个巨型飞机仓库的建设计划。飞机仓库的地面可以表示为 \(n\) 行 \(n\) 列的网格图,其中每个格子要么是空的,要么有障碍物。行从上到下依次被编号为 \(1\) 到 \(n\),列从左到右依次被编号为 \(1\) 到 \(n\)。
存放飞机零件的大型集装箱能在飞机仓库的地面上自由移动是很重要的。我们可以将每个集装箱看作一个以某个格子为中心的边平行于坐标轴的正方形。对于一个奇数 \(k\),一个尺寸为 \(k\) 的集装箱是一个包含 \(k\) 行 \(k\) 列的正方形。一个集装箱的坐标为其中心格子的坐标。集装箱可以向上下左右移动,但不能碰到障碍物,且不能移出仓库的边界。
给定 \(q\) 对格子 \(A_k\) 和 \(B_k\),对于每对格子,请找到能从 \(A_k\) 移动到 \(B_k\) 的集装箱的最大尺寸,注意这个尺寸也要是一个奇数。
对于 \(100\%\) 的数据,保证:\(2\le n\le 1000\),\(1\le q\le 300000\)。
题目分析:
-
首先可以求出以每个格子为中心的格子有多大
可以二分,也可以八连通(dbq,八连通我不会/kk)
-
然后就变成了求一条路径,使得路径上的最小值最大
这里就用到了上面的扩展,先对于整个图求出最大生成树,然后两点之间倍增求解即可
代码:
我的代码应该是这种写法的最简洁并且最短的写法/cf
#define fi first
#define se second
#define LL long long
#define m_p make_pair
#define p_b push_back
#define PII pair<int,int>
const int INF=1e9+7;
const int N=1e3+7;
const int dx[5]={1,-1,0,0};
const int dy[5]={0,0,1,-1};
int sum[N][N],n,sz[N][N],q;
char s[N][N];
int cnt;
struct node{
int u,v,w;
}e[(N*N)<<2];
int f[N*N];
vector<PII> G[N*N];
int fa[N*N][30],mn[N*N][30],dep[N*N];
int find(int u) {return u==f[u]?u:f[u]=find(f[u]);}
bool cmp(node a,node b) {return a.w>b.w;}
bool check(int i,int j,int mid)
{
if (i-mid<1||j-mid<1||i+mid>n||j+mid>n) return 0;
if (sum[i+mid][j+mid]-sum[i-mid-1][j+mid]-sum[i+mid][j-mid-1]+sum[i-mid-1][j-mid-1]) return 0;
return 1;
}
int get_sz(int i,int j)
{
int l=0,r=n/2,mid,ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if (check(i,j,mid)) l=mid+1,ans=mid;
else r=mid-1;
}
return ans*2+1;
}
int get_num(int i,int j) {return (i-1)*n+j;}
void add(int u,int v,int w) {e[++cnt].u=u;e[cnt].v=v;e[cnt].w=w;}
void Kru()
{
sort(e+1,e+cnt+1,cmp);
for (int i=1;i<=n*n;i++) f[i]=i;
for (int i=1;i<=cnt;i++)
{
int x=find(e[i].u),y=find(e[i].v);
if (x!=y)
{
G[e[i].u].p_b(m_p(e[i].v,e[i].w));
G[e[i].v].p_b(m_p(e[i].u,e[i].w));
f[x]=y;
}
}
}
void dfs(int u,int fath)
{
dep[u]=dep[fath]+1;
for (int j=1;j<=20;j++)
{
fa[u][j]=fa[fa[u][j-1]][j-1];
mn[u][j]=min(mn[u][j-1],mn[fa[u][j-1]][j-1]);
}
for (auto s:G[u])
{
int v=s.fi;
if (v==fath) continue;
fa[v][0]=u;mn[v][0]=s.se;
dfs(v,u);
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for (int j=20;j>=0;j--) if (dep[fa[x][j]]>=dep[y]) x=fa[x][j];
if (x==y) return x;
for (int j=20;j>=0;j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
return fa[x][0];
}
int query(int x,int y)
{
int ans=INF,lca=LCA(x,y);
for (int j=20;j>=0;j--) if (dep[fa[x][j]]>=dep[lca]) ans=min(ans,mn[x][j]),x=fa[x][j];
for (int j=20;j>=0;j--) if (dep[fa[y][j]]>=dep[lca]) ans=min(ans,mn[y][j]),y=fa[y][j];
return ans;
}
signed main(){
scanf("%d",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
cin>>s[i][j];
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(s[i][j]=='#');
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (s[i][j]=='.')
sz[i][j]=get_sz(i,j);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (sz[i][j])
for (int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if (x<1||y<1||x>n||y>n) continue;
add(get_num(i,j),get_num(x,y),min(sz[i][j],sz[x][y]));
}
Kru();
for(int i=0;i<=20;++i) fa[1][i]=1,mn[1][i]=INF;
dfs(1,1);
scanf("%d",&q);
while(q--)
{
int x=read(),y=read(),xx=read(),yy=read();
x=get_num(x,y),y=get_num(xx,yy);
printf("%d\n",query(x,y));
}
return 0;
}