Solution 水筒
题目大意:一个(n imes m)的矩阵里有一些墙不能走,有一些建筑物,有一些地方是原野。每走过一格原野需要消耗一份水,建筑物可以将水壶补满。多次询问从一个建筑物到另一个建筑物水壶容量至少为多少。
Kruskal重构树
分析:
首先不难想到暴力,我们把所有可以相互到达的建筑物两两连边,如果我们要使得最大边权尽量小那么我们就要求最小生成树。证明考虑贪心:不断加边直到两点连通,这就是Kruskal的过程。
求树上两点间边权的最大值就是Kruskal重构树的板子题
但是如果我们暴力枚举所有建筑物无法承受,但是我们发现有些边是没有用的,我们可以考虑类似于缩点的方法。我们给每个建筑物一个颜色,然后同一颜色的点离它最近的加油站都相同
这样我们只需要枚举每个点以及和它相邻的点,如果颜色不同连边即可,这个可以在bfs的时候顺便计算,染色发现已经染过的不同色点就连边
#include <cstdio>
#include <vector>
#include <queue>
#include <utility>
#include <algorithm>
using namespace std;
const int maxn = 2e3 + 100,maxp = 4e5 + 100,maxdep = 25;
int n,m,p,q,vis[maxn][maxn],dis[maxn][maxn],deltax[] = {-1,0,1,0},deltay[] = {0,-1,0,1};
namespace Graph{
struct Edge{int from,to,dist;};
vector<Edge> G[maxp];
inline void addedge(int from,int to,int dist){G[from].push_back(Edge{from,to,dist});}
int faz[maxp][maxdep + 1],dep[maxp],vis[maxp],col[maxp],val[maxp],col_tot;
inline void init(int u){
vis[u] = 1;col[u] = col_tot;
for(int i = 1;i <= maxdep;i++)
faz[u][i] = faz[faz[u][i - 1]][i - 1];
for(auto e : G[u]){
if(e.to == faz[u][0])continue;
faz[e.to][0] = u;
dep[e.to] = dep[u] + 1;
init(e.to);
}
}
inline void init(){
for(int i = (p << 1) - 1;i >= 1;i--)
if(!vis[i])dep[i] = 1,col_tot++,init(i);
}
inline int lca(int x,int y){
if(dep[x] < dep[y])swap(x,y);
for(int i = maxdep;i >= 0;i--)
if(dep[faz[x][i]] >= dep[y])
x = faz[x][i];
if(x == y)return x;
for(int i = maxdep;i >= 0;i--)
if(faz[x][i] != faz[y][i])
x = faz[x][i],y = faz[y][i];
return faz[x][0];
}
inline int query(int x,int y){return col[x] != col[y] ? -1 : val[lca(x,y)];}
}
namespace Kruskal{
using Graph::Edge;
vector<Edge> Edges;
inline void addedge(int from,int to,int dist){Edges.push_back(Edge{from,to,dist});}
int f[maxp],sz;
inline void init(){for(int i = 1;i <= p;i++)f[i] = i;sz = p;}
inline int find(int x){return x == f[x] ? x : f[x] = find(f[x]);}
inline void kruskal(){
sort(Edges.begin(),Edges.end(),[](const Edge &a,const Edge &b){return a.dist < b.dist;});
init();
int tot = p,lim = (p << 1) - 1;
for(int i = 1;i <= lim;i++)f[i] = i;
for(auto e : Edges){
int fx = find(e.from),fy = find(e.to);
if(fx != fy){
f[fx] = f[fy] = ++tot;
Graph::val[tot] = e.dist;
Graph::addedge(tot,fx,0),Graph::addedge(tot,fy,0);
if(tot == lim)break;
}
}
}
}
inline bool check(int x,int y){return x >= 1 && x <= n && y >= 1 && y <= m;}
char mp[maxn][maxn];
struct Pos{int x,y;}building[maxp];
inline void bfs(){
queue<Pos> Q;
for(int i = 1;i <= p;i++)
Q.push(building[i]),vis[building[i].x][building[i].y] = i;
while(!Q.empty()){
Pos now = Q.front();Q.pop();
int x = now.x,y = now.y;
for(int i = 0;i < 4;i++){
int nx = x + deltax[i],ny = y + deltay[i];
if(!check(nx,ny) || mp[nx][ny] == '#' || vis[nx][ny] == vis[x][y])continue;
if(vis[nx][ny] && vis[x][y] != vis[nx][ny]){Kruskal::addedge(vis[x][y],vis[nx][ny],dis[x][y] + dis[nx][ny]);continue;}
vis[nx][ny] = vis[x][y];
dis[nx][ny] = dis[x][y] + 1;
Q.push(Pos{nx,ny});
}
}
}
int main(){
scanf("%d %d %d %d",&n,&m,&p,&q);
for(int i = 1;i <= n;i++)scanf("%s",mp[i] + 1);
for(int i = 1;i <= p;i++)scanf("%d %d",&building[i].x,&building[i].y);
bfs();
Kruskal::kruskal();
Graph::init();
for(int u,v,i = 1;i <= q;i++)
scanf("%d %d",&u,&v),printf("%d
",Graph::query(u,v));
return 0;
}