bzoj4242 水壶

https://www.lydsy.com/JudgeOnline/problem.php?id=4242

显然答案应该为建筑物构成的最小生成树中,两点间的最大路径;那么只要得到这棵最小生成树就可以用倍增在O(
QlogN)的时间内得到答案了。因此关键是求最小生成树。注意到是平面图,因此考虑用bfs求最小生成树。直接以
每个建筑为原点拓展显然不行,那么我们可以把每个建筑都加入队列一起拓展,那么对于一个点,一定会被其中的
一个建筑第一次拓展到,则令这个点为该建筑的“势力范围”。那么对于两个建筑“势力范围”边界上接触的点,
显然这两个建筑之间的边只能有边界上的点得到。因此就用这些点连边即可。显然这样得到是最小生成树。然后用
倍增求lca的方法得到答案即可。当然也可以用Krutal重构树来做。

#include<queue>
#include<cstdio>
#include<algorithm>
#define N 2005
#define P 200010
#define M 10000010
using namespace std;
char ch,B[1<<15],*S=B,*T=B;
#define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
inline int read()
{
int x=0,f=1;char ch=getc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}
int head[P],tot,n,all,W,H,m;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
bool map[N][N];
int belong[N][N],dis[N][N];
struct edge
{
int next,to,v;
}e[P<<1];
struct Edge{int x,y,z;}E[M];
bool operator<(Edge x,Edge y)
{
return x.z<y.z;
}
void add(int u,int v,int w)
{
e[++tot]=(edge){head[u],v,w};
head[u]=tot;
e[++tot]=(edge){head[v],u,w};
head[v]=tot;
}
struct node{int x,y;};
queue<node>q;
void bfs()
{
while(!q.empty())
{
int x=q.front().x;
int y=q.front().y;
q.pop();
for(int k=0;k<4;k++)
{
int nx=x+dx[k],ny=y+dy[k];
if(nx<1||nx>H||ny<1||ny>W)
continue;
if(map[nx][ny])
continue;
if(!belong[nx][ny])//如果这个点没有扩展过的话
{
belong[nx][ny]=belong[x][y];
dis[nx][ny]=dis[x][y]+1;
q.push((node){nx,ny});
}
else //如果已扩展过则两点可以连边
if(belong[nx][ny]<belong[x][y])
{
E[++all]=(Edge){belong[x][y],belong[nx][ny],dis[x][y]+dis[nx][ny]-2};
}
}
}
}
int fa[P][19],Log[P],deep[P],f[P],d[P][19];
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
void dfs(int x)
{
deep[x]=deep[fa[x][0]]+1;
for(int i=1;deep[x]>(1<<i);i++)
fa[x][i]=fa[fa[x][i-1]][i-1],
d[x][i]=max(d[x][i-1],d[fa[x][i-1]][i-1]);
for(int i=head[x];i;i=e[i].next)
if(e[i].to!=fa[x][0])
{
fa[e[i].to][0]=x;
d[e[i].to][0]=e[i].v;
dfs(e[i].to);
}
}
int getdis(int x,int y)
{
if(find(x)!=find(y))
return -1;
if(deep[x]<deep[y])
swap(x,y);
int t=deep[x]-deep[y],ans=0;
for(int i=Log[t];~i;i--)
if(t&(1<<i))
ans=max(ans,d[x][i]),x=fa[x][i];
if(x==y)return ans;
for(int i=Log[deep[x]];~i;i--)
if(fa[x][i]!=fa[y][i])
ans=max(ans,max(d[x][i],d[y][i])),
x=fa[x][i],y=fa[y][i];
return max(ans,max(d[x][0],d[y][0]));
}
int main()
{
H=read();W=read();n=read();m=read();
for(int i=2;i<=n;i++)Log[i]=Log[i>>1]+1;
char s;
for(int i=1;i<=H;i++)
for(int j=1;j<=W;j++)
{
while(s=getc(),s!='.'&&s!='#');
map[i][j]=(s=='#');
}
int x,y;
for(int i=1;i<=n;i++)
{
x=read();
y=read();
belong[x][y]=i;
dis[x][y]=1;
q.push((node){x,y});
}
bfs();
sort(E+1,E+all+1);
//将边权从小到大排序,因为对于两个建筑物可以用不同的边连起来,自然我们要用最小值。
for(int i=1;i<=n;i++) //并查集操作
f[i]=i;
for(int i=1;i<=all;i++)
{
int fx=find(E[i].x),fy=find(E[i].y);
if(fx!=fy)
{
f[fy]=fx;
add(E[i].x,E[i].y,E[i].z); //x到y连一条边长为z
}
}
for(int i=1;i<=n;i++)
if(!deep[i])
dfs(i);
while(m--)
printf("%d\n",getdis(read(),read()));
}

原文地址:https://www.cnblogs.com/cutemush/p/11735085.html