http://acm.uestc.edu.cn/#/problem/show/482
做了这道题目很有收获,一方面是熟练了优化连边的技巧,另一方面是对Dijkstra有了新的感悟。
关于优化连边的做法:
首先离散化,将1e9范围的数离散化,重新分配编号,这样最多有2e5个节点。
很显然,题目中的边是一种区间到单点的边,所以可以考虑线段树优化连边,可能是因为自己不够熟练,实现得不好,所以超内存了,但是看到有人这样做过了的。
但是有更好的优化方式,可以有更小常数的时空复杂度。
我们发现起点区间有一个性质,那就是右边界一定在最右边,一共有n个区间,所以就不用线段树优化了,可以后缀和优化,每个区间新建一个节点,直接区间到单点连边,然后每个区间和前面的区间连边,跑Dijkstra就好了。
关于对Dijkstra的新感悟:
主要是看了这篇博客的解法,在思考为什么正确的过程中对Dijkstra有了更深的认识。
http://blog.csdn.net/xinag578/article/details/50883997
博主说是广搜+优先队列优化,其实本质上就是Dijkstra的O(nlogn)优化啦。
n条边各用一次,因此最多产生n个新节点,放入优先队列中,时间复杂度O(nlogn)。
我一直疑惑为什么要挑一个t最小的点,然后把所有能用的边都用了,就可以得到正解。主要是疑惑为什么敢这么大胆地把用过的边都丢掉,难道保证这样做物尽其用了吗?
通过和Dijkstra的对比后发现,它们的做法其实没有任何区别,Dijkstra也是挑一个d最小的HeapNode,然后把所有从这个Node出发的边都用掉,更新其他节点。
由于每个节点只done一次,所以每条边只用一次,一共e条边,产生的新节点个数其实不太好估计,会有重复。仔细想了想最优是O(n)最差是O(n^2),但是那不重要,因为要取对数,平方就变成常数了,因此时间复杂度为O(elogn)。
下面讲一讲为什么敢这么大胆地把用过的边丢掉,即证明物尽其用了。
首先,边的作用就是为了更新节点,如果能保证这条边更新的情况是最完美的,那就必然物尽其用了。
何时才最完美呢?一条边的终端是固定的,边的长度也是固定的,那么只有当边的起始端的d是最小的时候,更新就是最完美的。
Dijkstra算法每次挑出d最小