BZOJ4070 [Apio2015]雅加达的摩天楼 【分块 + 最短路】

题目链接

BZOJ4070

题解

考虑暴力建图,将每个\(B_i\)向其能到的点连边,复杂度\(O(\sum \frac{n}{p_i})\),当\(p\)比较小时不适用
考虑优化建图,每个\(doge\)能移动的点实际上是一组模\(p\)同余的点,那么只要对每个\(p\)\(n\)个点,然后内部距离为\(p\)的点连边,然后每个点向原来的点连边,如果某个点有步长为\(p\)\(doge\),则原点向该点连边,这样子每一层点数和边数都是\(O(n)\)的,复杂度是\(O(pn)\),当\(p\)较小时使用

如此可以得出最终算法,分块处理
对于\(p\)较小的点优化建图,\(p\)较大的点暴力建图
跑最短路即可

如果是\(dijsktra\),复杂度\(O(n\sqrt{n}log(n\sqrt{n}))\)
但据说这里\(spfa\)快?

#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 4000005,maxm = 15000005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
int h[maxn],ne = 1;
struct EDGE{int to,nxt,w;}ed[maxm];
inline void build(int u,int v,int w){
    ed[++ne] = (EDGE){v,h[u],w}; h[u] = ne;
}
int n,m,B,d[maxn],inq[maxn],S,T;
queue<int> q;
void spfa(){
    int E = B * n + n;
    for (int i = 1; i <= E; i++) d[i] = INF;
    d[S] = 0; q.push(S); int u;
    while (!q.empty()){
        u = q.front(); q.pop(); inq[u] = false;
        Redge(u) if (d[to = ed[k].to] > d[u] + ed[k].w){
            d[to] = d[u] + ed[k].w;
            if (!inq[to]) q.push(to),inq[to] = true;
        }
    }
}
int main(){
    n = read(); m = read(); B = min((int)sqrt(n),100);
    for (int i = 1; i <= B; i++){
        for (int j = 1; j <= i; j++){
            for (int u = j; u + i <= n; u += i){
                build(i * n + u,i * n + u + i,1);
                build(i * n + u + i,i * n + u,1);
            }
        }
        for (int j = 1; j <= n; j++)
            build(i * n + j,j,0);
    }
    int x,p;
    for (int t = 1; t <= m; t++){
        x = read() + 1; p = read();
        if (t == 1) S = x;
        if (t == 2) T = x;
        if (p > B){
            for (int i = p,j = 1; x - i > 0; i += p,j++)
                build(x,x - i,j);
            for (int i = p,j = 1; x + i <= n; i += p,j++)
                build(x,x + i,j);
        }
        else build(x,p * n + x,0);
    }
    spfa();
    if (d[T] == INF) puts("-1");
    else printf("%d\n",d[T]);
    return 0;
}

转载于:https://www.cnblogs.com/Mychael/p/9185811.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值