• [USACO17DEC]Push a Box P


    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;
    }
    
  • 相关阅读:
    07 总结ProgressDialog 异步任务
    1. vim 的安装及配置
    debian 源设置 ( apt-get 不能安装)
    在Debian中安装VNC Server
    让BB-Black通过usb0上网
    常用的一些 linux 指令
    Linux下同一目录内文件和目录为什么不能同名?
    beaglebone black 与电脑互传文件(夹)
    永久修改 putty字体大小
    Beaglebone Black的引脚分配
  • 原文地址:https://www.cnblogs.com/Troverld/p/14621524.html
Copyright © 2020-2023  润新知