【BZOJ4242】水壶(克鲁斯卡尔重构树,BFS)
题面
BZOJ然而是权限题。
Description
JOI君所居住的IOI市以一年四季都十分炎热著称。
IOI市是一个被分成纵H*横W块区域的长方形,每个区域都是建筑物、原野、墙壁之一。建筑物的区域有P个,编号为1...P。
JOI君只能进入建筑物与原野,而且每次只能走到相邻的区域中,且不能移动到市外。
JOI君因为各种各样的事情,必须在各个建筑物之间往返。虽然建筑物中的冷气设备非常好,但原野上的日光十分强烈,因此在原野上每走过一个区域都需要1单位的水。此外,原野上没有诸如自动售货机、饮水处之类的东西,因此IOI市的市民一般都携带水壶出行。大小为x的水壶最多可以装x单位的水,建筑物里有自来水可以将水壶装满。
由于携带大水壶是一件很困难的事情,因此JOI君决定携带尽量小的水壶移动。因此,为了随时能在建筑物之间移动,请你帮他写一个程序来计算最少需要多大的水壶。
现在给出IOI市的地图和Q个询问,第i个询问(1<=i<=Q)为“在建筑物Si和Ti之间移动,最小需要多大的水壶?”,请你对于每个询问输出对应的答案。
Input
第一行四个空格分隔的整数H,W,P,Q,表示IOI市被分成了纵H*横W块区域,有P个建筑物,Q次询问。
接下来H行,第i行(1<=i<=H)有一个长度为W的字符串,每个字符都是’.’或’#’之一,’.’表示这个位置是建筑物或原野,’#’表示这个位置是墙壁。
接下来P行描述IOI市每个建筑物的位置,第i行(1<=i<=P)有两个空格分隔的整数Ai和Bi,表示第i个建筑物的位置在第Ai行第Bi列。保证这个位置在地图中是’.’
接下来Q行,第i行(1<=i<=Q)有两个空格分隔的整数Si和Ti,表示第i个询问为“在建筑物Si和Ti之间移动,最小需要多大的水壶?”
Output
输出Q行,第i行(1<=i<=Q)一个整数,表示在建筑物Si和Ti之间移动最小需要多大的水壶。
如果无法到达,输出-1。此外,如果不需要经过原野就能到达,输出0。
Sample Input
5 5 4 4
.....
..##.
.#...
..#..
.....
1 1
4 2
3 3
2 5
1 2
2 4
1 3
3 4
Sample Output
3
4
4
2
HINT
1<=H<=2000
1<=W<=2000
2<=P<=2*10^5
1<=Q<=2*10^5
1<=Ai<=H(1<=i<=P)
1<=Bi<=W(1<=i<=P)
(Ai,Bi)≠(Aj,Bj)(1<=i<j<=P)
1<=Si<Ti<=P(1<=i<=Q)
题解
好神仙的一道题目啊。
很明显的一点,这道题目就是要在网格图上面计算一个最小生成树,然后就变成了货车运输一样的做法,可以倍增或者是克鲁斯卡尔重构树。
后面的部分就是原题,所以不再考虑,只考虑怎么样构造网格图最小生成树。
我们对于每一个可行的起点一起\(bfs\),对于每一个格子记录两个东西:一个记录距离当前位置最近的建筑的距离,以及是哪一个建筑。
当你\(bfs\)到某个格子的时候,发现这个格子已经被其他建筑给标记过了,那么就可以直接从距离当前这个格子最近的建筑连向距离拓展出来的格子最近的那个建筑。
然而这题卡常,边数有\(4*2000*2000\),所以开一个\(vector\)记录所有可能的距离,然后把所有的距离全部挂在上面跑。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define pi pair<int,int>
#define mp make_pair
#define fr first
#define sd second
#define MAX 2020
#define pb push_back
#define N 200200
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int H,W,P,Q;
pi p[N];
vector<pi> E[MAX*MAX];
char g[MAX][MAX];
int dis[MAX][MAX],bel[MAX][MAX];
int d[4][2]={1,0,0,1,-1,0,0,-1};
struct Line{int v,next;}e[N<<2];
int h[N<<1],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
void bfs()
{
queue<pi> Q;
for(int i=1;i<=P;++i)Q.push(p[i]),bel[p[i].fr][p[i].sd]=i;
while(!Q.empty())
{
pi u=Q.front();Q.pop();
for(int i=0;i<4;++i)
{
pi v=mp(u.fr+d[i][0],u.sd+d[i][1]);
if(g[v.fr][v.sd]=='#'||v.fr<1||v.fr>H||v.sd<1||v.sd>W)continue;
if(!bel[v.fr][v.sd])
{
Q.push(v);
bel[v.fr][v.sd]=bel[u.fr][u.sd];
dis[v.fr][v.sd]=dis[u.fr][u.sd]+1;
}
else E[dis[v.fr][v.sd]+dis[u.fr][u.sd]].pb(mp(bel[v.fr][v.sd],bel[u.fr][u.sd]));
}
}
}
int f[N<<1];
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
int CNT,w[N<<1],dep[N<<1];
int fa[22][N<<1];
void dfs(int u)
{
dep[u]=dep[fa[0][u]]+1;
for(int i=h[u];i;i=e[i].next)
dfs(e[i].v);
}
void Kruskal()
{
for(int i=1;i<=P;++i)f[i]=i;CNT=P;
for(int i=0;i<=H*W;++i)
for(int j=0,l=E[i].size();j<l;++j)
{
int u=getf(E[i][j].fr),v=getf(E[i][j].sd);
if(u==v)continue;++CNT;
f[CNT]=f[u]=f[v]=CNT;w[CNT]=i;
Add(fa[0][u]=CNT,u);Add(fa[0][v]=CNT,v);
}
for(int i=1;i<=21;++i)
for(int j=1;j<=CNT;++j)
fa[i][j]=fa[i-1][fa[i-1][j]];
}
int LCA(int u,int v)
{
if(dep[u]<dep[v])swap(u,v);
for(int i=21;~i;--i)
if(dep[fa[i][u]]>=dep[v])u=fa[i][u];
if(u==v)return u;
for(int i=21;~i;--i)
if(fa[i][u]!=fa[i][v])
u=fa[i][u],v=fa[i][v];
return fa[0][u];
}
int main()
{
H=read();W=read();P=read();Q=read();
for(int i=1;i<=H;++i)scanf("%s",g[i]+1);
for(int i=1;i<=P;++i)p[i].fr=read(),p[i].sd=read();
bfs();Kruskal();for(int i=CNT;i;--i)if(!dep[i])dfs(i);
int cnt=0;
while(Q--)
{
++cnt;
int u=read(),v=read();
if(getf(u)!=getf(v))puts("-1");
else printf("%d\n",w[LCA(u,v)]);
}
return 0;
}