魔幻暴力题。
题意简述
给你一张
n
×
m
n\times m
n×m 的地图,每个点是海 .
,岛屿 #
或者火山 v
。保证岛屿和非岛屿均可以形成恰好一个四连通块且岛屿不与地图边界接壤,至少有一个岛屿点与一个火山点。
定义一条合法的路径为,从一个非岛屿的点
s
s
s 出发,每次向四联通的非岛屿点走一格,用航线包围岛屿一整圈后回到点
s
s
s。一条路径包围岛屿定义为,不存在一条从岛屿出发的八连通路径可以在不触及路径的前提下到达边界。一条路径的权值为途中经过的所有点到离其最近的火山的曼哈顿距离的最小值。
现有
q
q
q 次询问,每次给定一个点
(
x
,
y
)
(x,y)
(x,y),你需要求出从
(
x
,
y
)
(x,y)
(x,y) 出发的合法路径的最大权值。
3 ≤ n , m ≤ 1 0 5 , 9 ≤ n m ≤ 3 × 1 0 5 , q ≤ 5 3\leq n,m\leq 10^5,9\leq nm\leq 3\times 10^5,q\leq 5 3≤n,m≤105,9≤nm≤3×105,q≤5。
解题思路
求最小值的最大值,具有单调性,可以二分。难点在于如何 check。
先跑一遍多源最短路把每个点离其最近的火山的曼哈顿距离算出来,记为 d i s i , j dis_{i,j} disi,j。若当前二分出来的答案为 k k k,则我们从询问的点 ( x , y ) (x,y) (x,y) 出发,向四个方向走,只能走 d i s dis dis 值大于等于 k k k 的点,将它们打上标记。
如何判断是否将岛屿围起来?考虑从岛屿点出发,向周围八个方向走,如果能够走到边界且路径上不经过被标记的点,则岛屿没有被完全包围。这个套路在ABC219 E中也用到了。那题也是个暴力题。
于是我们就做完了。注意空间的问题,我 vector 用的不太熟练,被卡了好几发。
代码示例
#include<bits/stdc++.h>
using namespace std;
int n,m,Q,stx,sty;
int dx[10]={0,0,1,-1,1,-1,-1,1},dy[10]={1,-1,0,0,1,-1,1,-1};
string s[100010];
vector<int> dis[100010];
vector<bool> Mark[100010],vis[100010];
struct node{
int x,y;
};
queue<node> q;
void Debug_dis(){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) cout<<dis[i][j]<<" ";
cout<<endl;
}
}
void Debug_Mark_points(int k){
cout<<"ans:"<<k<<endl;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) cout<<Mark[i][j]<<" ";
cout<<endl;
}
}
void Get_dis(){
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[i][j]=='v') dis[i][j]=0,q.push({i,j});
}
}
while(!q.empty()){
node u=q.front();q.pop();
for(int i=0;i<4;i++){
int tx=u.x+dx[i],ty=u.y+dy[i];
if(tx<1||tx>n||ty<1||ty>m) continue;
if(dis[tx][ty]>dis[u.x][u.y]+1){
dis[tx][ty]=dis[u.x][u.y]+1;
q.push({tx,ty});
}
}
}
}
void Mark_points(int k){
for(int i=1;i<=n;i++) Mark[i]=vector<bool>(m+5,0);
while(!q.empty()) q.pop();
q.push({stx,sty});
Mark[stx][sty]=1;
while(!q.empty()){
node u=q.front();q.pop();
for(int i=0;i<4;i++){
int tx=u.x+dx[i],ty=u.y+dy[i];
if(s[tx][ty]=='#') continue;
if(tx<1||tx>n||ty<1||ty>m||dis[tx][ty]<k||Mark[tx][ty]) continue;
Mark[tx][ty]=1;
q.push({tx,ty});
}
}
}
bool check(int k){
if(dis[stx][sty]<k) return 0;
Mark_points(k);
//if(k<=20) Debug_Mark_points(k);
for(int i=1;i<=n;i++) vis[i]=vector<bool>(m+5,0);
while(!q.empty()) q.pop();
int X=0,Y=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) if(s[i][j]=='#') X=i,Y=j;
}
vis[X][Y]=1;
q.push({X,Y});
while(!q.empty()){
node u=q.front();q.pop();
for(int i=0;i<8;i++){
int tx=u.x+dx[i],ty=u.y+dy[i];
if(tx<1||tx>n||ty<1||ty>m) return 0;
if(Mark[tx][ty]||vis[tx][ty]) continue;
vis[tx][ty]=1;
q.push({tx,ty});
}
}
return 1;
}
void C_close(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
}
int main(){
C_close();
cin>>n>>m>>Q;
for(int i=1;i<=n;i++) cin>>s[i],s[i]=" "+s[i];
for(int i=1;i<=n;i++) dis[i]=vector<int>(m+5,1e6);
Get_dis();
//Debug_dis();
while(Q--){
cin>>stx>>sty;
int l=0,r=n+m+5,ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
cout<<ans<<endl;
}
return 0;
}