码迷,mamicode.com
首页 > 其他好文 > 详细

[USACO17DEC]Push a Box P

时间:2021-04-07 11:24:06      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:思想   efi   box   problem   vector   cpu   break   错误   不用   

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;
}

[USACO17DEC]Push a Box P

标签:思想   efi   box   problem   vector   cpu   break   错误   不用   

原文地址:https://www.cnblogs.com/Troverld/p/14621524.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!