V.[USACO17DEC]Push a Box P
思想很简单,发现任意推动箱子的时刻牛总在箱子旁,而这总共是 (4nm) 种状态,可以建图储存,然后在上面搜索,搜出所有从起始状态可以到达的状态即可。我们需要连的边只有牛推了一格箱子的边(这个非常简单)以及牛不推箱子,从箱子的一方走到另一方的边。
于是我们要支持查询从一点到另一点不经过指定的某一点是否可行。一开始我的想法是建出圆方树后判定第三点是否在前两点的路径上。这个可以通过求两点间路径长度来判定。但是这样如果要保证复杂度的话,LCA就成为了瓶颈。(O(n)-O(1)) LCA是存在的,但是我也不会。后来想想看每次询问的两个点应该不会隔太远,所以就暴力上了发树剖。当然是TLE了,开O2后又变成了MLE,于是又费尽心思用指针垃圾回收不用的数组,终于不MLE了,又回到了TLE。
后来想想看,实际上就等价于判定二者是否在一个点双里。判定点双,只需要建出圆方树后,判断其是否是兄弟(有着共同的方点父亲)或者爷孙(方点是一个的儿子,一个的父亲)关系即可。于是把树剖删了,还是TLE。
然后继续卡。在建圆方树的父子关系时,我是用 vector
存边然后搜父亲的;但是实际上只需要在搜出一个点双后,令割点为方点父亲,其余点为方点儿子即可。这样就省了一个 vector
和一次搜索。吸氧后终于不TLE了,然后WA11。
数据下来一看,发现自己之前代码中有个错误,在计算初始状态时,因为起点和终点不一定紧贴,所以上述思考不一定正确,有可能起点与终点相邻的格子并不在同一个点双内,但是它们间唯一的路径也并不需要经过终点。所以就又加了发爆搜,卡过去了。
时间复杂度 (O(nm))。常数大得要死。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,q,dx[4]={1,0,-1,0},dy[4]={0,1,0,-1},cnt;
char s[1510][1510];
#define valid(x,y,z) (x+dx[z]>=0&&x+dx[z]<n&&y+dy[z]>=0&&y+dy[z]<m&&s[x+dx[z]][y+dy[z]]!='#')
int id(int x,int y,int z){return z*n*m+x*m+y;}
int id(int x,int y){return x*m+y;}
#define ID(x,y,z) (x+dx[z])*m+(y+dy[z])
vector<int>v[9000010];
int fa[4500010],dfn[2250010],low[2250010],stk[2250010],tp,tot;
bool VCC(int x,int y){return fa[x]==fa[y]||fa[x]!=-1&&fa[fa[x]]==y||fa[y]!=-1&&fa[fa[y]]==x;}
void Tarjan(int x){
dfn[x]=low[x]=++tot,stk[++tp]=x;
for(auto y:v[x]){
if(!dfn[y]){
Tarjan(y),low[x]=min(low[x],low[y]);
if(dfn[x]>low[y])continue;
while(stk[tp]!=y)fa[stk[tp--]]=cnt;
fa[stk[tp--]]=cnt;
fa[cnt++]=x;
}else low[x]=min(low[x],dfn[y]);
}
}
void init(){
for(int i=0;i<n;i++)for(int j=0;j<m;j++){
if(s[i][j]=='#')continue;
for(int k=0;k<4;k++)if(valid(i,j,k))v[id(i,j)].push_back(ID(i,j,k));
}
for(int i=0;i<n*m;i++)if(!dfn[i])Tarjan(i),tp--;
}
int ax,ay,bx,by;
bool vis[9000010];
void dfs(int x){
vis[x]=true;
for(auto y:v[x])if(!vis[y])dfs(y);
}
int col[1510][1510],ccc;
void dfs(int x,int y){
col[x][y]=ccc;
for(int z=0;z<4;z++)if(valid(x,y,z)&&!col[x+dx[z]][y+dy[z]])dfs(x+dx[z],y+dy[z]);
}
int main(){
// freopen("P4082_6.out","w",stdout);
scanf("%d%d%d",&n,&m,&q),cnt=n*m;
for(int i=0;i<n;i++)scanf("%s",s[i]);
memset(fa,-1,sizeof(fa)),init();
for(int i=0;i<=4*n*m;i++)v[i].clear();
for(int i=0;i<n;i++)for(int j=0;j<m;j++)if(s[i][j]!='#'&&!col[i][j])ccc++,dfs(i,j);
for(int i=0;i<n;i++)for(int j=0;j<m;j++){
if(s[i][j]=='#')continue;
for(int k=0;k<4;k++)if(valid(i,j,k)&&valid(i,j,k^2))v[id(i,j,k)].push_back(k*n*m+(i+dx[k^2])*m+(j+dy[k^2]));
for(int k=0;k<4;k++)for(int p=k+1;p<4;p++){
if(!valid(i,j,k)||!valid(i,j,p))continue;
if(col[i+dx[k]][j+dy[k]]!=col[i+dx[p]][j+dy[p]])continue;
if(!VCC(ID(i,j,k),ID(i,j,p)))continue;
v[id(i,j,k)].push_back(id(i,j,p));
v[id(i,j,p)].push_back(id(i,j,k));
}
if(s[i][j]=='A')ax=i,ay=j;
if(s[i][j]=='B')bx=i,by=j;
}
memset(col,0,sizeof(col)),ccc=1;
s[bx][by]='#';
dfs(ax,ay);
s[bx][by]='B';
for(int i=0;i<4;i++){
if(!valid(bx,by,i))continue;
if(!col[bx+dx[i]][by+dy[i]])continue;
v[4*n*m].push_back(id(bx,by,i));
}
dfs(4*n*m);
for(int i=1,x,y;i<=q;i++){
scanf("%d%d",&x,&y),x--,y--;
bool ok=false;
for(int k=0;k<4;k++)if(valid(x,y,k)&&vis[id(x,y,k)]){ok=true;break;}
if(x==bx&&y==by)ok=true;
puts(ok?"YES":"NO");
}
return 0;
}