P7473 [NOI Online 2021 入门组] 重力球

P7473 [NOI Online 2021 入门组] 重力球

题意

给你一个正方形平面,某些位置有障碍,对于平面上两个球,每次你可以改变重力方向使两个球下落到最底端,求使两个球位置重合的最小改变重力次数。障碍固定,多次询问两个球的位置。

思路

考虑最暴力的想法,总共有 \(n^4\) 种状态,即两个球的坐标。

考虑优化状态数,发现只有障碍物(边界)旁边(四联通)的位置才有用。实际最大位置数为 \(250\times 4+250\times 4=2000\) 左右。那么实际状态数最大为 \(2000\times 2000=4000000\) 左右。

我们把这些状态看做点,每个点只能有四条出边,那么边数和点数同阶。这样我们就有了另外一个暴力的想法:对于每个初始局面,暴力 BFS。

因为有多组询问考虑优化。实际上最终重合的状态有 \(2000\) 个,我们反向建边,然后从这些点开始 BFS 出到所有状态的最小代价。每次查询的时候枚举第一次改变重力的方向即可。(注意判重合)

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
inline int read(){
	int w=0,x=0;char c=getchar();
	while(!isdigit(c))w|=c=='-',c=getchar();
	while(isdigit(c))x=x*10+(c^48),c=getchar();
	return w?-x:x;
}
namespace star
{
	const int maxn=255,maxm=2010;
	int n,m,Q,tot,a[maxn][maxn],t[maxn][maxn][4];
	unsigned dis[maxm][maxm];
	vector<int> to[maxm][4];
	inline void addedge(int a,int b,int c){to[a][c].push_back(b);}
	inline void work(){
		n=read(),m=read(),Q=read();
		while(m--) a[read()][read()]=-1;
		for(int i=1;i<=n;a[0][i]=a[i][0]=a[n+1][i]=a[i][n+1]=-1,i++) for(int j=1;j<=n;j++)
			if(!a[i][j] and (a[i-1][j]==-1 or a[i+1][j]==-1 or a[i][j-1]==-1 or a[i][j+1]==-1))
				a[i][j]=++tot;
		for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) t[i][j][0]=a[i][j-1]==-1?a[i][j]:t[i][j-1][0],t[i][j][1]=a[i-1][j]==-1?a[i][j]:t[i-1][j][1];
		for(int i=n;i;i--) for(int j=n;j;j--) t[i][j][2]=a[i][j+1]==-1?a[i][j]:t[i][j+1][2],t[i][j][3]=a[i+1][j]==-1?a[i][j]:t[i+1][j][3];
		for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[i][j]>0) for(int k=0;k<4;k++) addedge(t[i][j][k],a[i][j],k);
		static pair<int,int> q[maxm*maxm];
		int hd=0,tl=0;
		memset(dis,-1,sizeof dis);
		for(int i=1;i<=tot;i++) q[++tl]=make_pair(i,i),dis[i][i]=1;
		while(hd<tl){
			pair<int,int> x=q[++hd];
			for(int i=0;i<4;i++) for(auto u:to[x.first][i]) for(auto v:to[x.second][i]) if(dis[u][v]==-1) dis[u][v]=dis[x.first][x.second]+1,q[++tl]=make_pair(u,v);
		}
		while(Q--){
			int x1=read(),y1=read(),x2=read(),y2=read();
			if(x1==x2 and y1==y2) puts("0");
			else printf("%d\n",min({dis[t[x1][y1][0]][t[x2][y2][0]],dis[t[x1][y1][1]][t[x2][y2][1]],dis[t[x1][y1][2]][t[x2][y2][2]],dis[t[x1][y1][3]][t[x2][y2][3]]}));
		}
	}
}
signed main(){
	star::work();
	return 0;
}
posted @ 2021-03-29 16:35  Star_Cried  阅读(165)  评论(0编辑  收藏  举报