【BZOJ 4070】 [Apio2015]雅加达的摩天楼

4070: [Apio2015]雅加达的摩天楼

Time limit: 1000 ms

Memory limit: 262144 KB

Description

The city of Jakarta has N skyscrapers located on a line, conveniently numbered 0 through N-1 from left to right. There are no other skyscrapers in Jakarta.

Jakarta is inhabited by M mystical creatures called “doge”s. The doges are conveniently numbered 0 through M-1. Doge i initially resides in skyscraper Bi. Doge i has a mystical power, represented with a positive integer Pi. This mystical power enables doges to jump between skyscrapers. In a single jump, a doge with superpower p that is currently in skyscraper b can move to either skyscraper b+p (if 0 ≤ b+p < N) or skyscraper b-p (if 0 ≤ b-p < N).

Doge 0 is the most awesome doge, and it is the leader of all the doges. It has an urgent news for doge 1, and wants the news to reach doge 1 as quickly as possible. Any doge that has received the news can do any of the following actions:

Make a jump to move to some other skyscraper.
Pass the news to another doge in the same skyscraper.
Please help the doges by calculating the minimum number of total jumps required by all doges to pass the news to doge 1, or if it is impossible to do so.

Input Format

The first line contains two integers N and M. Each of the next M lines contains two integers Bi and Pi.

Output Format

A single line containing the minimum number of total jumps, or -1 if it is impossible.

Sample Input

5 3
0 2
1 1
4 1
Sample Output

5
Explanation

Here is one of the possible scenarios to pass the news using 5 jumps:

Doge 0 jumps to skyscraper 2 and then to skyscraper 4 (2 jumps).
Doge 0 passes the news to doge 2.
Doge 2 jumps to skyscraper 3, and then to skyscraper 2, and then to skyscraper 1 (3 jumps).
Doge 2 passes the news to doge 1.
Subtasks

For each subtask,

0 ≤ Bi < N
Subtask 1 (10 points)

1 ≤ N ≤ 10
1 ≤ Pi ≤ 10
2 ≤ M ≤ 3
Subtask 2 (12 points)

1 ≤ N ≤ 100
1 ≤ Pi ≤ 100
2 ≤ M ≤ 2,000
Subtask 3 (14 points)

1 ≤ N ≤ 2,000
1 ≤ Pi ≤ 2,000
2 ≤ M ≤ 2,000
Subtask 4 (21 points)

1 ≤ N ≤ 2,000
1 ≤ Pi ≤ 2,000
2 ≤ M ≤ 30,000
Subtask 5 (43 points)

1 ≤ N ≤ 30,000
1 ≤ Pi ≤ 30,000
2 ≤ M ≤ 30,000

分块+最短路。

这道题如果直接跑最短路,最坏可能会产生 n2 条边。

考虑分块:
Pi>n
可以直接暴力来做,因为最多只会有 nn 条边

Pin
从每个点来看,最多只有 n 种走法,所以一共只有 nn 条边。

我们可以在后面添加一些辅助点来实现预处理:
枚举这 n 个长度,在后面分别建出 n <script type="math/tex" id="MathJax-Element-2215">n</script>个辅助点,连向前面对应的位置,同时连向可以来自和到达的辅助点。

对于读入直接连向他对应的辅助点即可。
(详见代码)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#define M 30005
#define pb push_back
#define LL long long
using namespace std;
queue<int> q;
int B,tot=0,cnt,s,t,h[M*110],m,n,inq[M*110];
int d[M*110],pos[105][M];
struct data
{
    int b,p;
}a[M];
struct edge
{
    int y,ne,v;
}e[M*500];
void Addedge(int x,int y,int v)
{
    e[++tot].y=y;
    e[tot].v=v;
    e[tot].ne=h[x];
    h[x]=tot;
}
void SPFA()
{
    for (int i=0;i<=cnt;i++)
        d[i]=1e9,inq[i]=0;
    q.push(a[0].b);
    d[a[0].b]=0,inq[a[0].b]=1;
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        inq[x]=0;
        for (int i=h[x];i;i=e[i].ne)
        {
            int y=e[i].y;
            if (d[y]>d[x]+e[i].v)
            {
                d[y]=d[x]+e[i].v;
                if (!inq[y]) q.push(y),inq[y]=1;
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=0;i<m;i++)
        scanf("%d%d",&a[i].b,&a[i].p);
    cnt=n-1;
    B=min((int)sqrt(n),100);
    for (int i=1;i<=B;i++)
        for (int j=0;j<i;j++)
            for (int k=j;k<n;k+=i)
            {
                pos[i][k]=++cnt;
                Addedge(cnt,k,0);
                if (k>=i)
                {
                    Addedge(cnt,cnt-1,1);
                    Addedge(cnt-1,cnt,1);
                }
            }
    for (int i=0;i<m;i++)
    {
        if (a[i].p<=B)
            Addedge(a[i].b,pos[a[i].p][a[i].b],0);
        else
        {
            for (int j=1;;j++)
                if (j*a[i].p+a[i].b>=n) break;
                else Addedge(a[i].b,j*a[i].p+a[i].b,j);
            for (int j=1;;j++)
                if (a[i].b-j*a[i].p<0) break;
                else Addedge(a[i].b,a[i].b-j*a[i].p,j);
        }
    }
    SPFA();
    if (d[a[1].b]==1e9)
        cout<<-1<<endl;
    else cout<<d[a[1].b]<<endl;
    return 0;
}

这里写图片描述

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值