1. 程式人生 > 其它 >P4082 - Push a Box 題解

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;
}
珍愛生命,遠離抄襲!