P3684 题解

分成几个部分处理。

一、预处理每个点的最大尺寸

特殊的,令障碍物的最大尺寸为 \(0\)

一个朴素的想法是二分。但这样是 \(O(n^2\log n)\) 的,太逊。

考虑把障碍全塞进一个队列里跑 bfs,这样可以求出每个点到一个障碍的最短路,显然这就是尺寸的一半(注意判一下不要超出边界)。

注意搜的时候要搜周围八连通的点!

复杂度 \(O(n^2)\)

二、建图

这个没啥好说的,暴力连边就行。

注意每个点最多会连出去四条边,而我们这里直接存图,不用邻接表,所以要开到 \(4n^2\),建树用的邻接表要开到 \(8n^2\)

\(d_i\) 表示 \(i\) 的最大尺寸,那么我们把一条 \(u-v\) 的无向边的边权设为 \(\min(d_u,d_v)\)

三、求最大生成树

注意到题目里的要求等价于两个点在最大生成树上的最短边,求出最大生成树后倍增即可。

四、处理询问

直接查询起点和终点路径最大值即可(障碍物的情况在建图时已讨论)。

然后这个题就做完了。总体来说不难,代码难度也不是那么高,我写了 189lines,一遍过。时间复杂度 \(O(n^2\log n)\)

代码(精简至 183lines):

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define gt getchar
#define pt putchar
#define fst first
#define scd second
typedef long long ll;
const int N=1005;
const int dx[8]={-1,-1,-1,0,1,1,1,0};
const int dy[8]={-1,0,1,1,1,0,-1,-1};
const int d_x[4]={-1,1,0,0};
const int d_y[4]={0,0,-1,1};
using namespace std;
using namespace __gnu_pbds;
typedef pair<int,int> pii;
inline bool __(char ch){return ch>=48&&ch<=57;}
template<class T> inline void read(T &x){
	x=0;bool sgn=0;char ch=gt();
	while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
	while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
	if(sgn) x=-x;
}
template<class T,class ...T1> inline void read(T &x,T1 &...x1){
	read(x);
	read(x1...);
}
template<class T> inline void print(T x){
	static char st[70];short top=0;
	if(x<0) pt('-');
 	do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top) pt(st[top--]);
}
template<class T> inline void printsp(T x){
	print(x);
	putchar(' ');
}
template<class T> inline void println(T x){
	print(x);
	putchar('\n');
}
struct edge{
	int to,nxt,w;
}e[(N*N)<<3];
struct Edge{
	int u,v,w;
	Edge(int _u=0,int _v=0,int _w=0):u(_u),v(_v),w(_w){}
	inline bool operator<(const Edge &b)const{return w>b.w;}
}ed[(N*N)<<2];
int n,m,T,head[N*N],cnt;
char s[N][N];
inline int id(int i,int j){
	return (i-1)*n+j;
}
inline bool outmp(int x,int y){
	return x<1||x>n||y<1||y>n;
}
inline void add_edge(int f,int t,int w){
	e[++cnt].to=t;
	e[cnt].w=w;
	e[cnt].nxt=head[f];
	head[f]=cnt;
}
inline void add_double(int f,int t,int w){
	add_edge(f,t,w);
	add_edge(t,f,w);
}
namespace Init{
	int dis[N*N],siz[N*N];
	bool chk[N*N],vis[N*N];
	queue<pii>q;
	inline void mark(){
		for(int i=1;i<=n;++i){
			for(int j=1;j<=n;++j){
				if(s[i][j]=='#'){
					chk[id(i,j)]=1;
					siz[id(i,j)]=0;
					q.push(pii(i,j));
					vis[id(i,j)]=1;
				}
			}
		}
	}
	inline void bfs(){
		while(q.size()){
			int x=q.front().fst,y=q.front().scd;
			q.pop();
			for(int i=0;i<8;++i){
				int xx=x+dx[i],yy=y+dy[i];
				if(outmp(xx,yy)||vis[id(xx,yy)]) continue;
				vis[id(xx,yy)]=1;
				dis[id(xx,yy)]=dis[id(x,y)]+1;
				siz[id(xx,yy)]=min({dis[id(xx,yy)],xx,yy,n-xx+1,n-yy+1})*2-1;
				q.push(pii(xx,yy));
			}
		}
	}
	inline void init(){
		mark(); bfs();
		for(int i=1;i<=n;++i){
			for(int j=1;j<=n;++j){
				for(int k=0;k<4;++k){
					int x=i+d_x[k],y=j+d_y[k];
					if(!outmp(x,y)) ed[++m]=Edge(id(i,j),id(x,y),min(siz[id(i,j)],siz[id(x,y)]));
				}
			}
		}
	}
}
namespace MST{
	int fa[N*N],siz[N*N];
	int find(int x){
		if(x!=fa[x]) fa[x]=find(fa[x]);
		return fa[x];
	}
	inline bool merge(int x,int y){
		x=find(x),y=find(y);
		if(x==y) return 0;
		if(siz[y]>siz[x]) swap(x,y);
		fa[y]=x,siz[x]+=siz[y];
		return 1;
	}
	inline void kruskal(){
		for(int i=1;i<=n*n;++i) fa[i]=i,siz[i]=1;
		sort(ed+1,ed+m+1);
		for(int i=1;i<=m;++i){
			int u=ed[i].u,v=ed[i].v,w=ed[i].w;
			if(find(u)!=find(v)){
				add_double(u,v,w);
				merge(u,v);
			}
		}
	}
}
namespace Sol{
	int g[N*N][21],anc[N*N][21],dep[N*N];
	void dfs(int u,int fa){
		dep[u]=dep[fa]+1;
		for(int i=1;i<=20;++i){
			anc[u][i]=anc[anc[u][i-1]][i-1];
			g[u][i]=min(g[u][i-1],g[anc[u][i-1]][i-1]);
		}
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(v==fa) continue;
			anc[v][0]=u,g[v][0]=e[i].w;
			dfs(v,u);
		}
	}
	inline void build(){
		MST::kruskal();
		for(int i=0;i<=20;++i) anc[1][i]=1,g[1][i]=1e9;
		dfs(1,1);
	}
	inline int LCA(int u,int v){
		if(dep[u]<dep[v]) swap(u,v);
		for(int i=20;i>=0;--i) if(dep[anc[u][i]]>=dep[v]) u=anc[u][i];
		if(u==v) return u;
		for(int i=20;i>=0;--i) if(anc[u][i]!=anc[v][i]) u=anc[u][i],v=anc[v][i];
		return anc[u][0];
	}
	inline int calc(int u,int v){
		int ans=1e9,lca=LCA(u,v);
		for(int i=20;i>=0;--i) if(dep[anc[u][i]]>=dep[lca]) ans=min(ans,g[u][i]),u=anc[u][i];
		for(int i=20;i>=0;--i) if(dep[anc[v][i]]>=dep[lca]) ans=min(ans,g[v][i]),v=anc[v][i];
		return ans;
	}
	inline void solve(){
		int x,y,z,w;
		read(x,y,z,w);
		println(calc(id(x,y),id(z,w)));
	}
}
signed main(){
	read(n);
	for(int i=1;i<=n;++i) scanf("%s",s[i]+1);
	Init::init();
	Sol::build();
	read(T);
	while(T--) Sol::solve();
	return 0;
}
posted @ 2024-02-28 15:08  Southern_Dynasty  阅读(21)  评论(0编辑  收藏  举报