• 【bzoj4070】[Apio2015]雅加达的摩天楼 set+堆优化Dijkstra


    题目描述

    印尼首都雅加达市有 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。

    输入

    输入的第一行包含两个整数 N 和 M。

    接下来 M 行,每行包含两个整数 Bi 和 Pi。

    输出

    输出一行,表示所需要的最少步数。如果消息永远无法传递到 1 号 doge,输出 −1。

    样例输入

    5 3
    0 2
    1 1
    4 1

    样例输出

    5


    题解

    set+堆优化Dijkstra

    题目显然可以转化为最短路模型:每个doge所在点向它能够跳到的点连边,长度为步数,最后0号所在位置到1号所在位置的最短路即为答案。

    但是这样边数过大。考虑优化:

    如果多个doge的bi和bi%pi相同,即能够跳到的点相同,那么可以直接由离某个点最近的doge向该点连边;然后再在这里面位置相邻的doge间连边。即找到一个点的前驱后继位置,这两个位置(如果存在)向它连边。

    这样看似没有什么大的优化,其实复杂度直接下降了1个级别。考虑最坏情况下的边数:步长越小的连边越多,因此需要贪心地选择步长短的。当步长为$i$时$b%i$最多只有$i$个,每个连的边是$frac ni$级别的;而总的个数为$m$,所以只能选择$1~sqrt m$的步长,因此总的边数时$nsqrt m$。

    最后跑堆优化Dijkstra即可。

    时间复杂度$O(nsqrt nlog n)$

    #include <set>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <utility>
    #include <algorithm>
    #define N 30010
    using namespace std;
    typedef pair<int , int> pr;
    struct data
    {
    	int b , p , v;
    	bool operator<(const data &a)const {return p == a.p ? v == a.v ? b < a.b : v < a.v : p < a.p;}
    }a[N];
    set<int> ss;
    set<int>::iterator it;
    priority_queue<pr> q;
    int head[N] , to[N * 300] , len[N * 300] , next[N * 300] , cnt , dis[N] , vis[N];
    inline void add(int x , int y , int z)
    {
    	to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    }
    int main()
    {
    	int n , m , i , j , k , s , t , x;
    	scanf("%d%d" , &n , &m);
    	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &a[i].b , &a[i].p) , a[i].v = a[i].b % a[i].p;
    	s = a[1].b , t = a[2].b , sort(a + 1 , a + m + 1);
    	for(i = j = 1 ; i <= m ; i = j)
    	{
    		while(j <= m && a[i].p == a[j].p && a[i].v == a[j].v) j ++ ;
    		ss.clear();
    		for(k = i ; k < j ; k ++ ) ss.insert(a[k].b);
    		for(k = i ; k < j - 1 ; k ++ ) add(a[k].b , a[k + 1].b , (a[k + 1].b - a[k].b) / a[i].p) , add(a[k + 1].b , a[k].b , (a[k + 1].b - a[k].b) / a[i].p);
    		for(k = a[i].v ; k < n ; k += a[i].p)
    		{
    			it = ss.upper_bound(k);
    			if(it != ss.end()) add(*it , k , (*it - k) / a[i].p);
    			it = ss.lower_bound(k);
    			if(it != ss.begin()) it -- , add(*it , k , (k - *it) / a[i].p);
    		}
    	}
    	memset(dis , 0x3f , sizeof(dis)) , dis[s] = 0 , q.push(pr(0 , s));
    	while(!q.empty())
    	{
    		x = q.top().second , q.pop();
    		if(vis[x]) continue;
    		vis[x] = 1;
    		for(i = head[x] ; i ; i = next[i])
    			if(dis[to[i]] > dis[x] + len[i])
    				dis[to[i]] = dis[x] + len[i] , q.push(pr(-dis[to[i]] , to[i]));
    	}
    	printf("%d
    " , dis[t] == 0x3f3f3f3f ? -1 : dis[t]);
    	return 0;
    }
    

     

  • 相关阅读:
    虚拟机磁盘和OSNetworkManagement
    虚拟化的设计考量
    魔獸世界台服身份證ID生成器
    完美的Windows Server 2008 R2 SP1 模板
    Windows 脚本主机概述
    创建动态链接库的方法简介
    iPad3越狱方法
    关于用netsh.exe配置系统防火及网络
    金蝶客户端多用户远程桌面解决方案
    poj 1459 最大流(EK实现)
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7675887.html
Copyright © 2020-2023  润新知