题解 AT1221 【水筒】

题目链接

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;
} 
原文地址:https://www.cnblogs.com/colazcy/p/11825538.html