P4082 - Push a Box 题解

经典(?)的推箱子问题。

考虑记录 (箱子的位置, bessie 的位置) 这样的二元组当作状态,正确性肯定是可以的。但是状态是 \((nm)^2\) 这就不好了。但是容易观察到只有当箱子和 bessie 挨在一起的时候的状态才是我们需要关心的关键状态,因为这样才能推得动箱子。你 bessie 要是把箱子放着不动,自己到处游走,那谁管你去了那些景点旅游啊。

接下来要做的事情就是给这 \(4nm\) 个状态之间连边,把它们的连通性确定下来。初始状态显然最多只有 \(4\) 种,对结束位置显然也只有最多 \(4\) 种状态,最终这 \(16\) 对中至少一组连通,那答案就是 YES 否则是 NO

状态之间的连边只有两种:一种是往当前方向推一格,一种是 bessie 换个方位。前者比较简单,就看看前方有没有障碍物就行了(连的是有向边啊!)。后者的话,判的就是在原图基础上,把箱子的位置堵住(其实就是割掉这个点)然后两种 bessie 的位置是否连通。那这就是圆方树的拿手好戏了——因为把某个点割掉之后原图的分裂情况跟圆方树一致。我们只需要找出 \(4\) 种位置各在哪个儿子树里(或者是割掉的点的子树的补集),枚举一下然后用子树的时间戳区间判就可以了。复杂度是均摊的,不用担心,虽然常数有点大但常数瓶颈不在这里。

上面的后者连边中,并不是 \(4\) 种情况的每一对连通对都要连双向边,如果这样的话最多要连 \(2\dbinom{4}{2}=12\) 条边,平均每个点连出 \(3\) 条。其实可以找到每个连通类,然后用一个有向环连起来,这样每个点连出 \(1\) 条。不难发现结合前者和后者一共要连 \(8nm\) 条边,这大概到了 1e7 级别。而我个人不太喜欢链式前向星那东西,这题一开始也就用 vector 写了。那么被卡空间、卡时间无疑了。把 vector 换成链式前向星立刻就过了,而且跑得很快,使用空间也只有 ML 的一半——STL,尤其是 vector deque 之流,确实是奢侈品。下次要是再在有卡空、卡时风险的图论题里不果断用链式前向星,我就 dlxt 好吧!!(把 vector 换成前向星真的很麻烦,还不如一开始就写)

另外这题有个小坑。如果目的地就是箱子本来在的地方的话,你 bessie 甚至不需要能到达箱子周围。

code(其实 u1s1 链式前向星写的挺清爽的)
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=1510;
int n,m,qu;
char a[N][N];
int id(int x,int y){return (x-1)*m+y;}
struct addedge_nei{
	int sz,head[N*N],nxt[4*N*N],val[4*N*N];
	addedge_nei(){sz=0,memset(head,0,sizeof(head));}
	void ae(int x,int y){
		sz++,nxt[sz]=head[x],head[x]=sz,val[sz]=y;
	}
}nei;
int dfn[2*N*N],low[N*N],nowdfn;
int stk[N*N],top;
int cnt;
struct addedge_cnei{
	int sz,head[2*N*N],nxt[4*N*N],val[4*N*N];
	addedge_cnei(){sz=0,memset(head,0,sizeof(head));}
	void ae(int x,int y){
		sz++,nxt[sz]=head[x],head[x]=sz,val[sz]=y;
	}
}cnei;
void tar(int x,int fa=0){
	stk[top++]=x;
	dfn[x]=low[x]=++nowdfn;
	if(!fa&&nei.head[x]==0)return cnt++,cnei.ae(n*m+cnt,x),cnei.ae(x,n*m+cnt),void();
	for(int i=nei.head[x];i;i=nei.nxt[i]){
		int y=nei.val[i];
		if(y==fa){fa=-1;continue;}
		if(!dfn[y]){
			tar(y,x),low[x]=min(low[x],low[y]);
			if(dfn[x]<=low[y]){
				cnt++;
				while(true){
					int z=stk[--top];
					cnei.ae(n*m+cnt,z),cnei.ae(z,n*m+cnt);
					if(z==y)break;
				}
				cnei.ae(n*m+cnt,x),cnei.ae(x,n*m+cnt);
			}
		}
		else low[x]=min(low[x],dfn[y]);
	}
}
int mxdfn[2*N*N];
void dfs(int x,int fa=0){
	dfn[x]=mxdfn[x]=++nowdfn;
	for(int i=cnei.head[x];i;i=cnei.nxt[i]){
		int y=cnei.val[i];
		if(y==fa)continue;
		dfs(y,x);
		mxdfn[x]=mxdfn[y];
	}
}
const int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
const string str[]={"right","down","left","up"};
int id0(int x,int y){return x*4-y;}
struct addedge_nei0{
	int sz,head[4*N*N],nxt[8*N*N],val[8*N*N];
	addedge_nei0(){sz=0,memset(head,0,sizeof(head));}
	void ae(int x,int y){
		sz++,nxt[sz]=head[x],head[x]=sz,val[sz]=y;
	}
}nei0;
bool vis0[4*N*N];
void dfs1(int x){
	vis0[x]=true;
	for(int i=nei0.head[x];i;i=nei0.nxt[i]){
		int y=nei0.val[i];
		if(!vis0[y])dfs1(y);
	}
}
bool ok(int x,int y){return 1<=x&&x<=n&&1<=y&&y<=m&&a[x][y]!=‘#‘;}
bool vis[N*N];
void dfs0(int x,int ban){
	vis[x]=true;
	for(int i=nei.head[x];i;i=nei.nxt[i]){
		int y=nei.val[i];
		if(!vis[y]&&y!=ban)dfs0(y,ban);
	}
}
int main(){
	cin>>n>>m>>qu;
	for(int i=1;i<=n;i++)scanf("%s",a[i]+1);
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(a[i][j]!=‘#‘){
		if(i<n&&a[i+1][j]!=‘#‘)nei.ae(id(i,j),id(i+1,j)),nei.ae(id(i+1,j),id(i,j));
		if(j<m&&a[i][j+1]!=‘#‘)nei.ae(id(i,j),id(i,j+1)),nei.ae(id(i,j+1),id(i,j));
	}
	for(int i=1;i<=n*m;i++)if(!dfn[i])tar(i);
	memset(dfn,0,sizeof(dfn));nowdfn=0;
	for(int i=1;i<=n*m;i++)if(!dfn[i])dfs(i);
	int A,B;
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
		if(a[i][j]==‘A‘)A=id(i,j);
		if(a[i][j]==‘B‘)B=id(i,j);
	}
	dfs0(A,B);
	vector<int> init;
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(a[i][j]!=‘#‘){
		int x=id(i,j),in[4]={};
		for(int k=0;k<4;k++){
			int xn=i+dx[k],yn=j+dy[k];
			if(!ok(xn,yn)){in[k]=-1;continue;}
			if(x==B&&vis[id(xn,yn)])init.pb(id0(x,k));
			int now=0;
			for(int o=cnei.head[x];o;o=cnei.nxt[o]){
				now++;
				int y=cnei.val[o];
				if(dfn[y]<dfn[x])continue;
				if(dfn[y]<=dfn[id(xn,yn)]&&dfn[id(xn,yn)]<=mxdfn[y])in[k]=now;
			}
			int xx=i-dx[k],yy=j-dy[k];
			if(!ok(xx,yy))continue;
			nei0.ae(id0(x,k),id0(id(xx,yy),k));
		}
		for(int k=0;k<10;k++){
			vector<int> v;
			for(int o=0;o<4;o++)if(in[o]==k)v.pb(id0(x,o));
			for(int o=0;o+1<v.size();o++)nei0.ae(v[o],v[o+1]);
			if(v.size()>1)nei0.ae(v.back(),v[0]);
		}
	}
	for(int i=0;i<init.size();i++)dfs1(init[i]);
	while(qu--){
		int x,y;
		scanf("%d%d",&x,&y);
		bool yes=id(x,y)==B;
		for(int i=0;i<4;i++)yes|=vis0[id0(id(x,y),i)];
		puts(yes?"YES":"NO");
	}
	return 0;
}

P4082 - Push a Box 题解

上一篇:重载运算符调试模板


下一篇:使用阿里云虚机遇到的问题