BZOJ 4242 水壶(BFS建图+最小生成树+树上倍增)
题意
JOI君所居住的IOI市以一年四季都十分炎热著称。
IOI市是一个被分成纵H*横W块区域的长方形,每个区域都是建筑物、原野、墙壁之一。建筑物的区域有P个,编号为1...P。
JOI君只能进入建筑物与原野,而且每次只能走到相邻的区域中,且不能移动到市外。
JOI君因为各种各样的事情,必须在各个建筑物之间往返。虽然建筑物中的冷气设备非常好,但原野上的日光十分强烈,因此在原野上每走过一个区域都需要1单位的水。此外,原野上没有诸如自动售货机、饮水处之类的东西,因此IOI市的市民一般都携带水壶出行。大小为x的水壶最多可以装x单位的水,建筑物里有自来水可以将水壶装满。
由于携带大水壶是一件很困难的事情,因此JOI君决定携带尽量小的水壶移动。因此,为了随时能在建筑物之间移动,请你帮他写一个程序来计算最少需要多大的水壶。
现在给出IOI市的地图和Q个询问,第i个询问(1<=i<=Q)为“在建筑物Si和Ti之间移动,最小需要多大的水壶?”,请你对于每个询问输出对应的答案。
\(H,W<=2000,P,Q<=2e5.\)
思路
码农题。
将每个点以及每个点之间的距离变成边,则此题询问的即为两点\((u,v)\)之间路径的最大边最小是多少。
由图的最小生成树性质,即为该图的最小生成树中\((u,v)\)之间的最大边。所以找到该图的最小生成树之后用树上倍增求解即可。
枚举点对建图是\(O(P^2)\)的,由于有障碍物,经典的\(O(PlogP)\)曼哈顿最小生成树也行不通。
考虑从P个点开始进行一次BFS求出每个点的管辖范围,对于点的管辖范围之间有相邻的那些点,以那些点为中间点建立两个点之间的边,这样就可以把边的数量从\(P^2\)缩小到\(HW\)。
这样的范围是可以承受的,另外注意判断一下两个点之间不连通的情况。
代码
# include<bits/stdc++.h>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-8
# define MOD 1000000007
# define INF 1e9
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(register int i=a; i<=n; ++i)
# define FDR(i,a,n) for(register int i=a; i>=n; --i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
inline char nc(){
static char buf[1000000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int Scan(){
char ch=nc();int sum=0, f=1;
if (ch=='-') f=-1, ch=nc();
while(!(ch>='0'&&ch<='9'))ch=nc();
while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
return sum*f;
}
const int N=2005;
//Code begin....
const int M=200005;
const int DEG=20;
char s[N][N];
struct Node{int x, y;}node[M];
struct Q{int u, v, w;};
struct Edge{int p, next, w;}edge[M<<1];
int head[M], cnt=1, F[M];
int n, m, p, q;
int vis[N][N], id[N][N];
int fa[M][DEG], ma[M][DEG], deg[M];
vector<Q>E;
queue<Node>que;
queue<int>Que;
void add_edge(int u, int v, int w){
edge[cnt].p=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++;
}
bool comp(Q a, Q b){return a.w<b.w;}
int find(int x){return F[x]==0?x:(F[x]=find(F[x]));}
void BFS(){
Node tmp;
int ps[4][2]={1,0,-1,0,0,1,0,-1};
mem(vis,-1);
FOR(i,1,p) {
vis[node[i].x][node[i].y]=0; id[node[i].x][node[i].y]=i;
que.push(node[i]);
}
while (!que.empty()) {
tmp=que.front(); que.pop();
int dx, dy;
FOR(i,0,3) {
dx=tmp.x+ps[i][0], dy=tmp.y+ps[i][1];
if (dx<=0||dx>n||dy<=0||dy>m||s[dx][dy]=='#'||vis[dx][dy]!=-1) continue;
vis[dx][dy]=vis[tmp.x][tmp.y]+1; id[dx][dy]=id[tmp.x][tmp.y];
que.push(Node{dx,dy});
}
}
}
void Solve(){
FOR(i,1,n) FOR(j,1,m) {
if (j!=m&&vis[i][j]!=-1&&vis[i][j+1]!=-1&&id[i][j]!=id[i][j+1]) E.pb(Q{id[i][j],id[i][j+1],vis[i][j]+vis[i][j+1]});
if (i!=n&&vis[i][j]!=-1&&vis[i+1][j]!=-1&&id[i][j]!=id[i+1][j]) E.pb(Q{id[i][j],id[i+1][j],vis[i][j]+vis[i+1][j]});
}
sort(E.begin(),E.end(),comp);
}
void Kruscal(){
int u, v;
for (int i=0; i<E.size(); ++i) {
u=find(E[i].u); v=find(E[i].v);
if (u!=v) {
F[u]=v;
add_edge(E[i].u,E[i].v,E[i].w); add_edge(E[i].v,E[i].u,E[i].w);
}
}
}
void bfs(int root){
deg[root]=0; fa[root][0]=root; ma[root][0]=0; Que.push(root);
while (!Que.empty()) {
int tmp=Que.front(); Que.pop();
FOR(i,1,DEG-1) fa[tmp][i]=fa[fa[tmp][i-1]][i-1], ma[tmp][i]=max(ma[tmp][i-1],ma[fa[tmp][i-1]][i-1]);
for (int i=head[tmp]; i; i=edge[i].next) {
int v=edge[i].p;
if (v==fa[tmp][0]) continue;
deg[v]=deg[tmp]+1; fa[v][0]=tmp; ma[v][0]=edge[i].w; Que.push(v);
}
}
}
int LCA(int u, int v){
int ans=0;
if (deg[u]>deg[v]) swap(u,v);
int hu=deg[u], hv=deg[v], tu=u, tv=v;
for (int det=hv-hu, i=0; det; det>>=1, ++i) if (det&1) ans=max(ans,ma[tv][i]), tv=fa[tv][i];
if (tu==tv) return ans;
for (int i=DEG-1; i>=0; --i) {
if (fa[tu][i]==fa[tv][i]) continue;
ans=max(ans,ma[tu][i]); ans=max(ans,ma[tv][i]);
tu=fa[tu][i]; tv=fa[tv][i];
}
return max(ans,max(ma[tu][0],ma[tv][0]));
}
int main ()
{
int u, v;
scanf("%d%d%d%d",&n,&m,&p,&q);
FOR(i,1,n) scanf("%s",s[i]+1);
FOR(i,1,p) scanf("%d%d",&node[i].x,&node[i].y);
BFS();
Solve();
Kruscal();
mem(deg,-1);
FOR(i,1,M) if (deg[i]==-1) bfs(i);
while (q--) {
scanf("%d%d",&u,&v);
if (find(u)!=find(v)) puts("-1");
else printf("%d\n",LCA(u,v));
}
return 0;
}