zoukankan      html  css  js  c++  java
  • 【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
    ",dis[T]);
    }
    
    int main()
    {
        init();
        spfa();
    
        return 0;
    }
  • 相关阅读:
    本地Grails配置与MyEclipse配置
    Linux下Apache James 邮件安装与发送程序
    MyEclipse8.6 安装groovy插件
    系统管理指南:基本管理 第21 章• 使用Sun PatchManager 管理Solaris 修补程序(任务)
    tar.bz2 解压命令。
    系统管理指南:基本管理 索引
    系统管理指南:基本管理 第22 章• 使用patchadd 命令管理Solaris 修补程序(任务)~附录A • SMF 服务
    如何安装gcc
    系统管理指南:基本管理 第20 章• 管理Solaris 修补程序和更新(概述)
    系统管理指南:基本管理 第18 章• 用Solaris 系统管理工具管理软件(任务)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793335.html
Copyright © 2011-2022 走看看