题意
印尼首都雅加达市有 N 座摩天楼,它们排列成一条直线,我们从左到右依次将它们编号为 0 到 N−1。除了这 N 座摩天楼外,雅加达市没有其他摩天楼。
有 M 只叫做 “doge” 的神秘生物在雅加达市居住,它们的编号依次是 0 到 M−1。编号为 i 的 doge 最初居住于编号为 Bi 的摩天楼。每只 doge 都有一种神秘的力量,使它们能够在摩天楼之间跳跃,编号为 i 的 doge 的跳跃能力为 Pi (Pi>0)。
在一次跳跃中,位于摩天楼 b 而跳跃能力为 p 的 doge 可以跳跃到编号为 b−p (如果 0≤b−p < N)或 b+p (如果 0≤b+p < N)的摩天楼。
编号为 0 的 doge 是所有 doge 的首领,它有一条紧急的消息要尽快传送给编 号为 1 的 doge。任何一个收到消息的 doge 有以下两个选择:
跳跃到其他摩天楼上;
将消息传递给它当前所在的摩天楼上的其他 doge。
请帮助 doge 们计算将消息从 0 号 doge 传递到 1 号 doge 所需要的最少总跳跃步数,或者告诉它们消息永远不可能传递到 1 号 doge。
1≤N≤30000
1≤Pi≤30000
2≤M≤30000
分析
一眼最短路。但是显然建图会炸。
考虑用分块来优化。
设块大小为block,那么给每一个点新开block个辅助点,分别表示跳的距离,然后在可以到达的辅助点之间连边,那么边数就不超过nsqrt(n)。
如果p[i]<=block,那么就从p[i]连一条边到p[i]的第b[i]个辅助点。
如果p[i]>block,就暴力连边即可。
注意空间限制。
分块的思想真的很棒棒啊,还有doge到底是smg。。。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int N=3030005;
const int inf=0x3f3f3f3f;
int n,block,cnt,last[N],dis[N],m,s,t;
struct edge{int to,next,w;}e[N*5];
queue<int> q;
bool vis[N];
int point(int x,int y)
{
return x*n+y;
}
void addedge(int u,int v,int w)
{
e[++cnt].to=v;e[cnt].w=w;e[cnt].next=last[u];last[u]=cnt;
}
void spfa()
{
for (int i=1;i<=block*n+n;i++) dis[i]=inf;
dis[s]=0;vis[s]=1;
q.push(s);
while (!q.empty())
{
int u=q.front();
q.pop();
for (int i=last[u];i;i=e[i].next)
if (dis[u]+e[i].w<dis[e[i].to])
{
dis[e[i].to]=dis[u]+e[i].w;
if (!vis[e[i].to]) vis[e[i].to]=1,q.push(e[i].to);
}
vis[u]=0;
}
}
int main()
{
scanf("%d%d",&n,&m);
block=min(100,(int)sqrt(n));
for (int i=1;i<=block;i++)
for (int j=1;j<=n;j++)
addedge(point(i,j),j,0);
for (int i=1;i<=block;i++)
for (int j=1;j<=n-i;j++)
addedge(point(i,j),point(i,j+i),1),addedge(point(i,j+i),point(i,j),1);
for (int i=1;i<=m;i++)
{
int pos,p;
scanf("%d%d",&pos,&p);
pos++;
if (i==1) s=pos;
else if (i==2) t=pos;
if (p<=block) addedge(pos,point(p,pos),0);
else
{
for (int j=1;pos+j*p<=n;j++) addedge(pos,pos+j*p,j);
for (int j=1;pos-j*p>0;j++) addedge(pos,pos-j*p,j);
}
}
spfa();
if (dis[t]==inf) dis[t]=-1;
printf("%d",dis[t]);
return 0;
}