BZOJ5138 Push a Box题解(bfs+圆方树)

题目:BZOJ5138.
题目大意:给定一张 n ∗ m n*m nm的有障碍点的网格,分别有两个位置有人和箱子.现在给出 q q q个询问,每次询问从初始状态是否让人把箱子推到某个位置.
1 ≤ n , m ≤ 1.5 ∗ 1 0 3 , 1 ≤ q ≤ 5 ∗ 1 0 4 1\leq n,m\leq 1.5*10^3,1\leq q\leq 5*10^4 1n,m1.5103,1q5104.

看到这道题我们可以想到用bfs来求出一个数组 v i s [ x ] [ y ] [ k ] vis[x][y][k] vis[x][y][k]表示箱子在 ( x , y ) (x,y) (x,y)且人在箱子的 0 / 1 / 2 / 3 0/1/2/3 0/1/2/3方向的状态是否可能到达,但是在箱子位置确定时人还需要用一个bfs来求出是否可以从某个方向到另一个方向,需要 O ( n 2 m 2 ) O(n^2m^2) O(n2m2)才可以实现.

但是仔细考虑一下,会发现若要从 ( x , y ) (x,y) (x,y)的某个方向到另一个方向,原来没有箱子时必然有只有一条需要经过 ( x , y ) (x,y) (x,y)的路径.而现在 ( x , y ) (x,y) (x,y)被堵上了,相当于需要在 ( x , y ) (x,y) (x,y)没被堵上时有两条路径,这个问题抽象到图上就是需要这两个方向所在的点在同一个点双连通分量内.

到这一步后,我们可以考虑用一个圆方树来维护这个问题,两个点在同一个点双内只有可能时这两个点在圆方树上有相同的父亲或某个点是另一个点的爷爷,构造出圆方树上的父亲指针即可.

时间复杂度 O ( n m ) O(nm) O(nm).

代码如下:

#include<bits/stdc++.h>
using namespace std;
 
#define Abigail inline void
typedef long long LL;
 
const int N=1500;
const int dx[4]={1,-1,0,0};
const int dy[4]={0,0,1,-1};
 
char s[N+9];
int n,m,cq,b[N+9][N+9];
int px,py,bx,by,vis[N+9][N+9][5];
struct state{
  int x,y,g;
  state(int X=0,int Y=0,int G=0){x=X;y=Y;g=G;}
};
queue<state>q;
 
bool Out_map(int x,int y){return x<1||x>n||y<1||y>m;}
 
void Bfs_vis4(int stx,int sty){
  vis[stx][sty][4]=1;q.push(state(stx,sty));
  while (!q.empty()){
    state t=q.front();q.pop();
    for (int i=0;i<4;++i){
      int nx=t.x+dx[i],ny=t.y+dy[i];
      if (Out_map(nx,ny)||!b[nx][ny]||vis[nx][ny][4]) continue;
      vis[nx][ny][4]=1;q.push(state(nx,ny));
    }
  }
}
 
struct side{
  int y,next;
}e[N*N*8+9];
int lin[N*N+9],cs;
 
int H(int x,int y){return (x-1)*m+y;}
void Ins(int x,int y){e[++cs].y=y;e[cs].next=lin[x];lin[x]=cs;}
void Ins2(int x,int y){Ins(x,y);Ins(y,x);}
 
void Get_graph(){
  for (int i=1;i<=n;++i)
    for (int j=1;j<=m;++j)
      for (int k=0;k<2;++k){
        if (!b[i][j]) continue;
        int nx=i+dx[k<<1],ny=j+dy[k<<1];
        if (Out_map(nx,ny)||!b[nx][ny]) continue;
        Ins2(H(i,j),H(nx,ny));
      }
}
 
int dfn[N*N+9],low[N*N+9],co;
stack<int>sta;
int cn,fa[N*N*2+9];
 
void Tarjan(int k){
  dfn[k]=low[k]=++co;
  int flag=0;
  sta.push(k); 
  for (int i=lin[k];i;i=e[i].next)
    if (!dfn[e[i].y]){
      Tarjan(e[i].y);
      if (low[e[i].y]>=dfn[k]){
        ++flag;
        fa[++cn]=k;
        while (2333){
          int t=sta.top();sta.pop();
          fa[t]=cn;
          if (t==e[i].y) break;
        }
      }
      low[k]=min(low[k],low[e[i].y]);
    }else low[k]=min(low[k],dfn[e[i].y]);
}
 
void Build(){
  cn=n*m;
  for (int i=1;i<=n*m;++i)
    if (!dfn[i]) Tarjan(i);
}
 
void Bfs_vis0123(int stx,int sty,int stg){
  vis[stx][sty][stg]=1;q.push(state(stx,sty,stg));
  while (!q.empty()){
    state t=q.front();q.pop();
    int nx=t.x+dx[t.g],ny=t.y+dy[t.g];
    if (!Out_map(nx,ny)&&b[nx][ny]&&!vis[nx][ny][t.g])
      vis[nx][ny][t.g]=1,q.push(state(nx,ny,t.g));
    for (int i=0;i<4;++i){
      if (vis[t.x][t.y][i]) continue;
      nx=t.x-dx[i],ny=t.y-dy[i];
      if (Out_map(nx,ny)) continue;
      int x=H(t.x-dx[t.g],t.y-dy[t.g]),y=H(nx,ny);
      if (fa[x]^fa[y]&&fa[fa[x]]^y&&fa[fa[y]]^x) continue;
      vis[t.x][t.y][i]=1,q.push(state(t.x,t.y,i));
    }
  }
}
 
Abigail into(){
  scanf("%d%d%d",&n,&m,&cq);
  for (int i=1;i<=n;++i){
    scanf("%s",s+1);
    for (int j=1;j<=m;++j)
      switch (s[j]){
        case '#':
          b[i][j]=0;
          break;
        case '.':
          b[i][j]=1;
          break;
        case 'A':
          b[i][j]=1;
          px=i;py=j;
          break;
        case 'B':
          b[i][j]=1;
          bx=i;by=j;
          break;
      }
  }
}
 
Abigail work(){
  b[bx][by]=0;
  Bfs_vis4(px,py);
  b[bx][by]=1;
  for (int i=0;i<4;++i)
    if (vis[bx-dx[i]][by-dy[i]][4]) vis[bx][by][i]=1;
  Get_graph();
  Build();
  for (int i=0;i<4;++i)
    if (vis[bx][by][i]) Bfs_vis0123(bx,by,i);
}
 
Abigail getans(){
  for (int i=1;i<=cq;++i){
    int x,y;
    scanf("%d%d",&x,&y);
    puts(vis[x][y][0]||vis[x][y][1]||vis[x][y][2]||vis[x][y][3]||bx==x&&by==y?"YES":"NO");
  }
}
 
int main(){
  into();
  work();
  getans();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值