【bzoj4070】【APIO2015】【雅加达的摩天楼】【最短路】

Description

 印尼首都雅加达市有 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。

Input

输入的第一行包含两个整数 N 和 M。

接下来 M 行,每行包含两个整数 Bi 和 Pi。

Output

输出一行,表示所需要的最少步数。如果消息永远无法传递到 1 号 doge,输出 −1。

Sample Input

5 3
0 2
1 1
4 1

Sample Output

5
explanation
下面是一种步数为 5 的解决方案:
0 号 doge 跳跃到 2 号摩天楼,再跳跃到 4 号摩天楼(2 步)。
0 号 doge 将消息传递给 2 号 doge。
2 号 doge 跳跃到 3 号摩天楼,接着跳跃到 2 号摩天楼,再跳跃到 1 号摩天楼(3 步)。
2 号 doge 将消息传递给 1 号 doge。

HINT

 子任务


所有数据都保证 0≤Bi<N。


子任务 1 (10 分)

1≤N≤10

1≤Pi≤10

2≤M≤3

子任务 2 (12 分)

1≤N≤100

1≤Pi≤100

2≤M≤2000

子任务 3 (14 分)

1≤N≤2000

1≤Pi≤2000

2≤M≤2000

子任务 4 (21 分)

1≤N≤2000

1≤Pi≤2000

2≤M≤30000

子任务 5 (43 分)

1≤N≤30000

1≤Pi≤30000

2≤M≤30000
题解:
         朴素建图边数是n^2级别的.显然过不了
         考虑分块.
         对于p>sqrt(n) 
         我们直接建图即可.
         对于p<=sqrt(n)
         我们新建sqrt(n)层图.第i层图代表p为i时的联通情况.
         对于一个doge,如果p<=sqrt(n)那就把它连向新建出的第p层图中的点,边权为0.
         同样的新建的图中的点也要连回原图中的点.边权为0.
         最后跑一遍spfa即可.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 3100000
#define M 15000010
#define inf 707406378
using namespace std;
int point[N],st,en,n,m,a,b,next[M],dis[N],f[N],q[N],cnt,t,num,pos[110][30010];
struct use{
  int en,v;
}e[M]; 
void add(int x,int y,int v){
  //cout<<x<<' '<<y<<' '<<v<<endl;
  next[++cnt]=point[x];point[x]=cnt;
  e[cnt].en=y;e[cnt].v=v;
}
void spfa(){
  int h(0),t(1);
  memset(dis,127/3,sizeof(dis));
  dis[st]=0;q[t]=st;f[st]=1;
  while (h<t){
  	h++;if (h==N) h=0;int u=q[h];f[u]=0;
  	for (int i=point[u];i;i=next[i])
  	  if (dis[e[i].en]>dis[u]+e[i].v){
  	    dis[e[i].en]=dis[u]+e[i].v;
  	    if (!f[e[i].en]){
  	       t++;if (t==N) t=0;
		   q[t]=e[i].en;
  	       f[e[i].en]=1;
  	    }
  	  }
  }
}
int main(){
  scanf("%d%d",&n,&m);num=n-1;
  int t=min((int)sqrt(n),100);
  for (int i=1;i<=t;i++) 
    for (int j=0;j<i;j++)
      for (int k=j;k<n;k+=i){
        pos[i][k]=++num;
        add(num,k,0);
		if (k>=i){add(num-1,num,1);add(num,num-1,1);}  
      }
  for (int i=1;i<=m;i++){
    scanf("%d%d",&a,&b);
    if (i==1) st=a;if (i==2) en=a;
    if (b>t){
      for (int j=1;j*b+a<n;j++) add(a,j*b+a,j);
      for (int j=1;a-j*b>=0;j++) add(a,a-j*b,j);
    }
    else add(a,pos[b][a],0);
  }
  spfa();
  if (dis[en]!=inf) printf("%d\n",dis[en]);
  else printf("-1\n");
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值