[BZOJ4242]水壶

Posted by Dispwnl on September 2, 2018

题目(权限题)

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$每个城市将附近的原野染色,每个原野记录最近的城市为谁(即颜色)和到最近的城市的距离

如果从$i$城市到达了$j$原野并且$j$原野没染色,就把$j$原野染成$i$;如果已经染色并且颜色为$k$,就求出了$i$到$k$之间的距离(即$i\rightarrow j+k\rightarrow j$)

因为边长不超过$H*W$,所以可以用$vector$存每个边,这样就不用$sort$了

这题卡常卡到恶心($bzoj$6元CPU果然虚),原来我用$string$存的字符串,后面改成$char[]$就过了QAQ

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<vector>
# include<queue>
# include<algorithm>
using namespace std;
const int MAX=4e5+5,MAXN=2e3+5;
struct p{
	int x,y;
}c[MAX],point[MAX];
int n,m,num,tot,cnt,P,Q;
int mv1[4]={1,-1,0,0};
int mv2[4]={0,0,1,-1};
int h[MAX],fa[MAX],val[MAX],d[MAX];
int col[MAXN][MAXN],dis[MAXN][MAXN],f[MAX][22];
char A[MAXN][MAXN];
queue<p> qu;
vector<p> ve[MAX*10];
int read()
{
	int x=0;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
	return x;
}
void bfs()
{
	while(!qu.empty())
	{
		p tt=qu.front();
		qu.pop();
		for(int i=0;i<=3;++i)
		  {
		  	int xx=tt.x+mv1[i],yy=tt.y+mv2[i];
		  	if(A[xx][yy]=='#'||xx<1||yy<1||xx>n||yy>m) continue;
		  	if(!col[xx][yy]) qu.push((p){xx,yy}),col[xx][yy]=col[tt.x][tt.y],dis[xx][yy]=dis[tt.x][tt.y]+1;
		  	else ve[dis[xx][yy]+dis[tt.x][tt.y]].push_back((p){col[xx][yy],col[tt.x][tt.y]});
		  }
	}
}
int find(int x)
{
	if(x!=fa[x]) fa[x]=find(fa[x]);
	return fa[x];
}
void add(int x,int y)
{
	c[++num]=(p){h[x],y},h[x]=num;
}
void dfs(int x,int f)
{
	d[x]=d[f]+1;
	for(int i=h[x];i;i=c[i].x)
	  dfs(c[i].y,x);
}
int LCA(int x,int y)
{
	if(d[x]<d[y]) swap(x,y);
	for(int i=21;i>=0;--i)
	  if(d[f[x][i]]>=d[y]) x=f[x][i];
	if(x==y) return x;
	for(int i=21;i>=0;--i)
	  if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
int main()
{
	n=read(),m=read(),cnt=P=read(),Q=read();
	for(int i=1;i<=n;++i)
	  scanf("%s",A[i]+1);
	for(int i=1;i<=P;++i)
	  fa[i]=i,point[i].x=read(),point[i].y=read(),col[point[i].x][point[i].y]=i,qu.push(point[i]);
	bfs();
	int Num=0;
	for(int i=0;i<=n*m;++i)
	  {
	  	if(Num==P-1) break;
	  	for(int j=0;j<ve[i].size();++j)
	      {
	    	int r1=find(ve[i][j].x),r2=find(ve[i][j].y);
			if(r1==r2) continue;
	    	++Num,val[++cnt]=i,fa[cnt]=fa[r1]=fa[r2]=cnt,f[r1][0]=f[r2][0]=cnt;
	    	add(cnt,r1),add(cnt,r2);
		  }
	  }
	for(int i=1;i<=21;++i)
	  for(int j=1;j<=cnt;++j)
	    f[j][i]=f[f[j][i-1]][i-1];
	for(int i=cnt;i>=1;--i)
	  if(!d[i]) dfs(find(i),0);
	for(int i=1;i<=Q;++i)
	  {
	  	int x=read(),y=read();
	  	if(find(x)!=find(y)) printf("-1\n");
	  	else printf("%d\n",val[LCA(x,y)]);
	  }
	return 0;
}