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

题面

题目链接

题解

首先这题放在图论专题难度就下降了一大个档次ww

我们注意到一点,就是如果你可以把两个点的位置记下来,一步操作之后,只会有四种可能的情况.

两个点的位置情况乍一看是 \(n^4\approx 3.9\times10^9\) 存不下,但是注意到一步操作之后,一定会在障碍点四周四个点或者边界上.

所以情况数只有 \((4m+4n)^2=4\times10^6\),完全可以接受.

然后我们的思路就是先预处理出所有点移动一步可能的位置,建图,bfs 求出每个点最小的步数,然后回答询问的时候枚举一下第一步的情况.

小常识:边权为1的图的最短路可以用bfs \(O(n)\)

到多个点的最小步数直接把所有终点塞进去bfs就好了

时间复杂度 \(O((4m+4n)^2+q)\)

呃这个代码实现不太好写(我的码力问题

实现上的小细节

记得建的是反边,回答询问的时候记得特判两个点最开始是否已经重合.

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 255, M = 4e6+5, inf = 1e9;
int n,m,q,np;
struct point 
{ 
	int x,y;
	void rd() { scanf("%d%d",&x,&y); }
	void print() { printf("(%d,%d)",x,y); }
	friend bool operator< (const point &a, const point &b) { return a.x<b.x or (a.x==b.x and a.y<b.y); }
	friend bool operator== (const point &a, const point &b) {return a.x==b.x and a.y==b.y; } 
	friend point operator+ (const point &a, const point &b) { return {a.x+b.x,a.y+b.y}; } 
	friend point operator- (const point &a, const point &b) { return {a.x-b.x,a.y-b.y}; } 
} p[N<<3];

int a[N][N][4],dis[M],mp[N][N];
inline int p_to_q(int x, int y) { return (x-1)*np+y; }
int head[M],nxt[M<<4],ver[M<<4],tot;
void add(int x, int y) { ver[++tot]=y; nxt[tot]=head[x]; head[x]=tot; }

void init()
{
	//init p
	static point obs[N],dir[]={{1,0},{0,1},{-1,0},{0,-1}};
	static int vt[N][N];
	auto check=[&](point t) { return 1<=t.x and t.x<=n and 1<=t.y and t.y<=n and !vt[t.x][t.y]; };
	for(int i=1; i<=m; i++) obs[i].rd(),vt[obs[i].x][obs[i].y]=1;
	for(int i=1; i<=n; i++)
	{
		if(check({1,i})) p[++np]={1,i};
		if(check({n,i})) p[++np]={n,i};
		if(check({i,1})) p[++np]={i,1};
		if(check({i,n})) p[++np]={i,n};
	}
	for(int i=1; i<=m; i++) for(int j=0; j<4; j++)
	{
		point t=obs[i]+dir[j];
		if(check(t)) p[++np]=t;
	}
	sort(p+1,p+np+1); np=unique(p+1,p+np+1)-p-1;
	for(int i=1; i<=np; i++) mp[p[i].x][p[i].y]=i;
	//init a
	for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) 
		if(check({i,j})) for(int k=0; k<4; k++)
		{
			point c={i,j};
			while(check(c+dir[k])) c=c+dir[k];
			a[i][j][k]=mp[c.x][c.y];
		}
	//init q
	for(int i=1; i<=np; i++) for(int j=1; j<=np; j++)
		for(int k=0; k<4; k++)
		{
			int u=p_to_q(i,j); 
			int v=p_to_q(a[p[i].x][p[i].y][k],a[p[j].x][p[j].y][k]);
			add(v,u);
		}
}
void work_dist()
{
	queue<int> q;
	static int vis[M];
	for(int i=1; i<=np*np; i++) dis[i]=inf;
	for(int i=1; i<=np; i++) { int t=p_to_q(i,i); q.push(t); dis[t]=0; vis[t]=1; }
	while(!q.empty())
	{
		int u=q.front(); q.pop();
		for(int j=head[u]; j; j=nxt[j])
		{
			int v=ver[j]; 
			if(!vis[v]) dis[v]=min(dis[v],dis[u]+1),vis[v]=1,q.push(v);
		}
	}
}
void answer_query()
{
	point S,T;
	while(q--)
	{
		S.rd(); T.rd(); int res=inf;
		if(S==T) res=0;
		else if(mp[S.x][S.y] and mp[T.x][T.y]) res=dis[p_to_q(mp[S.x][S.y],mp[T.x][T.y])];
		else for(int j=0; j<4; j++) res=min(res,1+dis[p_to_q(a[S.x][S.y][j],a[T.x][T.y][j])]);
		printf("%d\n",res>=inf?-1:res);
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	init(); work_dist(); answer_query();
	return 0;
}

posted @ 2022-11-11 21:40  copper_carbonate  阅读(41)  评论(0编辑  收藏  举报