【BZOJ4070】雅加达的摩天楼(APIO2015)-分块+最短路

测试地址:雅加达的摩天楼
题目大意:N个点排成一排,有M只狗,每只狗有一个跳跃能力P,初始它在点B,这只狗每步可以往左或往右跳P个点。现在要从0号狗传信息到1号狗,一只狗可以传信息给同个点的其他狗,问最少跳几步。
做法:本题需要用到分块+最短路。
看到这题,第一个想法就是,从每只狗所在的点B向所有点B+xP连边权为|x|的边,然后跑最短路。然而我们发现,这样建图最多会连出mn条边,时间和空间上都不能承受。
还有一种想法,就是用类似于网络流的思想,先把每个点拆成n个点,这样就形成一个分层图,第i层内点x和点x+i间连有边权为1的双向边,但是为了保证每条路径都是一个合法的方案,我们不能直接在层与层之间连边,而是需要对每个点再拆出一个点,从原点拆出的其他点向这个点连边权为0的边,接着,如果点i有一条跳跃能力为P的狗,就从新拆出的点向原先拆出的第P个点连边权为0的边。这样就是合法的了,然而边数仍然是n2的,不能承受。
观察数据范围,容易想到用分块的思想分类讨论,从而把这两种暴力中和成一种更优的做法。在第一种做法中,一只跳跃能力为P的狗,能连出nP条边,而在第二种做法中,每拆出一层的点就要连2n条边。于是对于P>n的狗,采用第一种做法连边,而对于其它的狗,采用第二种做法连边。第二种做法中最后拆出的一层点和第一种做法中的点重合。这样就最多会连出O(mn)条边了,于是就可以用SPFA做了。
最后要注意的是,块的大小不能太大,否则会MLE,100是合适的。
以下是本人代码(洛谷AC,BZOJ上T了,有待解决):

#include <bits/stdc++.h>
using namespace std;
const int N=30000;
const int inf=1000000000;
int n,m,S,T,blocksiz;
int first[N*200+10]={0},tot=0,dis[N*200+10];
bool vis[N*200+10]={0};
struct edge
{
    int v,next,d;
}e[N*500];
queue<int> Q;

int point(int x,int y)
{
    return x*(blocksiz+1)+y+1;
}

void insert(int a,int b,int d)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    e[tot].d=d;
    first[a]=tot;
}

void init()
{
    scanf("%d%d",&n,&m);
    blocksiz=min((int)(sqrt(n)+1),100);
    for(int i=0;i<m;i++)
    {
        int b,p;
        scanf("%d%d",&b,&p);
        if (i==0) S=point(b,0);
        if (i==1) T=point(b,0);
        if (p>blocksiz)
        {
            for(int j=1;b+j*p<n;j++)
                insert(point(b,0),point(b+j*p,0),j);
            for(int j=1;b-j*p>=0;j++)
                insert(point(b,0),point(b-j*p,0),j);
        }
        else insert(point(b,0),point(b,p),0);
    }

    for(int i=1;i<=blocksiz;i++)
    {
        for(int j=0;j<n;j++)
            insert(point(j,i),point(j,0),0);
        for(int j=0;j<i;j++)
        {
            for(int k=1;j+k*i<n;k++)
            {
                insert(point(j+(k-1)*i,i),point(j+k*i,i),1);
                insert(point(j+k*i,i),point(j+(k-1)*i,i),1);
            }
        }
    }
}

void spfa()
{
    for(int i=1;i<=(blocksiz+1)*n;i++)
        dis[i]=inf;
    dis[S]=0;
    Q.push(S);
    vis[S]=1;
    while(!Q.empty())
    {
        int v=Q.front();Q.pop();
        for(int i=first[v];i;i=e[i].next)
            if (dis[v]+e[i].d<dis[e[i].v])
            {
                dis[e[i].v]=dis[v]+e[i].d;
                if (!vis[e[i].v])
                {
                    Q.push(e[i].v);
                    vis[e[i].v]=1;
                }
            }
        vis[v]=0;
    }

    if (dis[T]==inf) printf("-1");
    else printf("%d\n",dis[T]);
}

int main()
{
    init();
    spfa();

    return 0;
}
posted @ 2018-06-12 16:32  Maxwei_wzj  阅读(172)  评论(0编辑  收藏  举报