bzoj 4070: [Apio2015]雅加达的摩天楼 最短路+分块

本文介绍了一种基于分块优化的最短路径算法解决特定问题的方法,即通过设计特殊的辅助节点来减少图的复杂度,进而求解传递消息所需的最少跳跃次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

印尼首都雅加达市有 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值