当前位置: 首页 > news >正文

(分块思想、最短路)洛谷 P3645 雅加达的摩天楼

题意

印尼首都雅加达市有 N N N 座摩天楼,依次将它们编号为 0 0 0 N − 1 N − 1 N1

M M M 只叫做 “doge” 的神秘生物,编号依次是 0 0 0 M − 1 M − 1 M1。编号为 i i i 的 doge 最初居住于编号为 B i B_i Bi 的摩天楼。每只 doge 都有一种神秘的力量,使它们能够在摩天楼之间跳跃,编号为 i i i 的 doge 的跳跃能力为 P i P_i Pi P i > 0 P_i > 0 Pi>0)。

在一次跳跃中,位于摩天楼 b b b 而跳跃能力为 p p p 的 doge 可以跳跃到编号为 b − p b - p bp (如果 0 ≤ b − p < N 0 \leq b - p < N 0bp<N)或 b + p b + p b+p (如果 0 ≤ b + p < N 0 \leq b + p < N 0b+p<N)的摩天楼。

编号为 0 0 0 的 doge 是所有 doge 的首领,它有一条紧急的消息要尽快传送给编号为 1 1 1 的 doge。任何一个收到消息的 doge 有以下两个选择:

  • 跳跃到其他摩天楼上;
  • 将消息传递给它当前所在的摩天楼上的其他 doge。

请帮助 doge 们计算将消息从 0 0 0 号 doge 传递到 1 1 1 号 doge 所需要的最少总跳跃步数,或者告诉它们消息永远不可能传递到 1 1 1 号 doge。如果消息永远无法传递到 1 1 1 号 doge,输出 − 1 −1 1

输入

5 3
0 2
1 1
4 1

输出

5

【样例解释】

下面是一种步数为 5 5 5 的解决方案:

0 0 0 号 doge 跳跃到 2 2 2 号摩天楼,再跳跃到 4 4 4 号摩天楼( 2 2 2 步)。

0 0 0 号 doge 将消息传递给 2 2 2 号 doge。

2 2 2 号 doge 跳跃到 3 3 3 号摩天楼,接着跳跃到 2 2 2 号摩天楼,再跳跃到 1 1 1 号摩天楼( 3 3 3 步)。

2 2 2 号 doge 将消息传递给 1 1 1 号 doge。

1 ≤ N ≤ 30000 1 \leq N \leq 30000 1N30000 1 ≤ P i ≤ 30000 1 \leq P_i \leq 30000 1Pi30000 2 ≤ M ≤ 30000 2 \leq M \leq 30000 2M30000

思路

如此跳跃,不妨看作直线上的“最短路”,一只 doge 跳到另外一只 doge 的摩天楼后,“换乘”另一只 doge 继续跳,那就是 bfs 了。

不难发现,跳跃过程中,最优情况是一只 doge 只使用一次,因为其它 doge 不会跟着 0 0 0 跳,总不能回去找那一只 doge 吧。

造一组数据:

10 3
0 3
5 4
7 3

那么 bfs 时,图大概长这样:
在这里插入图片描述
看我暴力 bfs,在洛谷狂砍 95 p t s \textrm{95}pts 95pts

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=3e4+5,inf=0x3f3f3f3f;
ll n,m,b[N],p[N];
vector<ll>a[N];
ll stp[N],ans=inf;
queue<ll>q;
void bfs(ll u,ll step)
{
	stp[u]=step;
	while(!q.empty())
	{
		ll v=q.front();
		q.pop();
		if(v==b[2])
		{
			ans=min(ans,step);
			return; 
		}
		for(auto x:a[v])
		{
			ll t=1;
			for(int i=v-x;i>=0;i-=x)
			{
				if(step+t<stp[i])
				{
					q.push(i);
					bfs(i,step+t);
				}
				t++;
			}
			t=1;
			for(int i=v+x;i<n;i+=x)
			{
				if(step+t<stp[i])
				{
					q.push(i);
					bfs(i,step+t);
				}
				t++;
			}
		}
	}
}
int main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld",&b[i],&p[i]);
		a[b[i]].push_back(p[i]); 
	}
	memset(stp,inf,sizeof(stp));
	q.push(b[1]);
	bfs(b[1],0);
	if(ans==inf)puts("-1");
	else printf("%lld",ans);
	return 0;
}

我们发现一只 doge 的跳跃能力 p p p 比较小的话,那就要建非常多的边,这显然不是我们想要的。

考虑真正使用最短路算法,并且建真正的边,我们尝试在建边上优化。

for(int i=b-p;i>=0;i-=p)for(int i=b+p;i<=n;i++) 中,既然容易被卡到 Θ ( n ) \Theta(n) Θ(n),那就只连跳跃能力 p i p_i pi 比较大的 doge。这时候分块思想就派上用场了:

  • p > n p>\sqrt{n} p>n ,那就按照上图样式建边,建边次数(循环次数)必然小于 n \sqrt{n} n

其它的怎么办呢?如果我们记录其它的 doge 的具体信息再建边,那和 Θ ( m n ) \Theta(mn) Θ(mn) 没有区别。因此我们用一个标记数组 m a r k \rm mark mark 记录跳跃能力在 [ 1 , n ] [1,\sqrt{n}] [1,n ] 的,是否出现过。

记录完之后怎么建这部分图呢?这时候我们考虑枚举 1 ∼ n 1\sim \sqrt{n} 1n 的步数 p ′ p' p,有被打上 m a r k \rm mark mark 标记的,我们就对每个点 i ∈ [ 0 , n − p ′ ) i\in[0,n-p') i[0,np),建一条 i ↔ i + p ′ i\leftrightarrow i+p' ii+p 的、边权为 1 1 1 的双向边,就不从起点开始枚举了。大概长这样:
在这里插入图片描述

注意不能再原来的“直线”上直接建,不然本来这个地方没有 doge 在这里你又让他“换乘”。于是考虑对每一个枚举的步数 p ′ p' p 建立分层图。那怎么进入分层图呢?我们在输入 b , p b,p b,p 的时候,在起点 b b b,建一个通往 分层图 p p p 所对应分层图编号 b p b_p bp 的、边权为 0 0 0 的入口“单向边”;出去的话,就在分层图上每个对应点,建一个通往原来“直线”的、边权为 0 0 0 的出口单向边,这样就可以实现 doge 的“换乘了”。
在这里插入图片描述
然后跑最短路,听说 dijkstra 会超时,那就 spfa 吧。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e4+5,M=2e7+9,inf=21e8;
int n,m,B[N];
bool mark[N];
int dis[M];
queue<int>q;
int head[M],idx;
struct edge
{
	int to,next,w;
}e[M<<1];
void addedge(int u,int v,int w)
{
	idx++;
	e[idx].to=v;
	e[idx].next=head[u];
	e[idx].w=w;
	head[u]=idx;
}
bool vis[M];
void bfs(int s)
{
	q.push(s);
	dis[s]=0;
	vis[s]=1;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=e[i].next)
		{
			int v=e[i].to,w=e[i].w;
			if(dis[u]+w<dis[v])
			{
				dis[v]=dis[u]+w;
				if(!vis[v])q.push(v);
			}
		}
	}
}
int fmp(int i,int p)//对应分层图编号:跳的步数 p 在 0~n-1 每个点之间跳跃 
{
	return p*n+i;
}
int main()
{
	scanf("%d%d",&n,&m);
	int bSize=min(10,(int)sqrt(n/3));
	//最优情况时,每只 doge 只会被用 1 次 
	for(int i=0;i<m;i++)
	{
		int b,p;
		scanf("%d%d",&b,&p);
		B[i]=b;
		if(p>bSize)//p较大,边数小于n/bSize 
		{
			int tick=1;
			for(int j=b-p;j>=0;j-=p)
			{
				addedge(b,j,tick);
				tick++;
			}
			tick=1;
			for(int j=b+p;j<n;j+=p)
			{
				addedge(b,j,tick);
				tick++;
			}
		}
		else 
		{
			addedge(b,fmp(b,p),0);//在 b 可以进入步数 p 所对应的分层图 
			mark[p]=1;
		}
	}
	for(int p=1;p<=bSize;p++)//较小步数 
	{
		if(!mark[p])continue;
		for(int i=0;i+p<n;i++)
		{
			addedge(fmp(i,p),fmp(i+p,p),1);//在每个步数 p 对应 的分层图上跳跃 
			addedge(fmp(i+p,p),fmp(i,p),1);
		}
		for(int i=0;i<n;i++)//返回上层图 
		addedge(fmp(i,p),i,0);
	}
	for(int i=0;i<M;i++)
	dis[i]=inf;
	bfs(B[0]);
	if(dis[B[1]]>=inf)puts("-1");
	else printf("%d",dis[B[1]]);
	return 0;
}

相关文章:

  • 谈谈空间复杂度考量,特别是递归调用栈空间消耗?
  • docker网桥问题导致ldap组件安装失败分析解决
  • 前端 技术栈
  • 从“泛读”到“精读”:合合信息文档解析如何让大模型更懂复杂文档?
  • Golang 当中 byte 和 rune 类型的区别
  • 将eclipse中的web项目导入idea
  • 3d pose 指标和数据集
  • Python第六章17:字典(dict)练习题
  • Varjo Base 将 Varjo XR-4系列支持扩展至2030年
  • yum install 报错(CentOS换源):
  • 喜报|迪捷软件入选工信部“2024年信息技术应用创新解决方案”
  • llamafactory微调效果与vllm部署效果不一致如何解决
  • 多线程编程:条件变量
  • 17--华为防火墙智能选路全解:网络世界的智能导航系统
  • Ubuntu平台下安装Node相关环境
  • MATLAB 2024b深度学习,图神经网络(GNN)
  • Spring Data审计利器:@LastModifiedDate详解!!!
  • 作业(7)
  • 网络空间安全(42)Windows实战篇
  • 3.28-3 文档读取和插入
  • 知名中医讲师邵学军逝世,终年51岁
  • 上市公司重大资产重组新规九要点:引入私募“反向挂钩”,压缩审核流程
  • 李伟任山东省委常委、省纪委书记
  • 刘小涛任江苏省委副书记
  • 国税总局上海市税务局回应刘晓庆被举报涉嫌偷漏税:正依法依规办理
  • “养胃骗局”大公开,真正有用的方法究竟是?