zoukankan      html  css  js  c++  java
  • 【学习笔记】分治法最短路小结

    简单的开头

    “分治法”和“最短路”两者似乎是毫不相干的东西,然而就是有毒瘤出题人将这两个整在了一起。

    但其实这还算是一类比较有意思的问题,于是就有了这一篇水文。

    一道简单题

    题目简述

    【NEERC2015】Distance on Triangulation

    你有一个凸 (n) 边形,并给定它的三角剖分。现有 (Q) 次询问,每次询问两个顶点,求两点间只经过 (n) 边形的边或三角剖分边,最少需要经过几条边。

    (4le nle 5 imes 10^4, 1le Qle 10^5)

    分析

    首先三角剖分的每条对角线都可以将整个多边形分为两部分。

    于是我们先选定一条剖分边 ((x, y)),将这个多边形分成两部分。

    然后我们可以解决所有询问点对在两部分内的询问:这种询问的最短路必定会经过 (x)(y) 点。

    怎么解决?只要分别在 (x, y) 点做一遍 Bfs 即可。这一部分是 (O( ext{多边形大小})) 的。

    但是显然就这样解决不了所有的询问,还是有的不会经过 (x)(y) 的。

    那就再分治下去!我们对两部分的多边形分别再做一次这个算法。

    不难得到,多边形分割得尽可能平均时,总复杂度为 (O(nlog n))

    小结

    一般来说,这类 多次询问最短路 的题目都可以想想 分治

    大概的套路就是把图分为两部分,然后 处理掉所有跨越中间的询问 即可。

    当然前提是这个图 可以被这样简单地分割

    一道经典题

    题目简述

    【ZJOI2016】旅行者

    给定一个 (n imes m) 个格点的网格,在每个格点 ((i, j)) 可以:

    • 花费 (r(i, j)) 走到 ((i, j+1))
    • 花费 (r(i, j - 1)) 走到 ((i, j-1))
    • 花费 (c(i, j)) 走到 ((i+1, j))
    • 花费 (c(i-1, j)) 走到 ((i-1, j))

    其中 (r, c) 给定且非负。现有 (Q) 个询问,每次询问两个格点间的最短路长。

    (1le n imes mle 2 imes 10^4, 1le Qle 10^5)

    分析

    我们可以想上面那题一样的套路来处理。

    上面我们选了一条边分割,这里我们选择一条直线将整个平面分为两块。

    上面我们对分割边的两个端点都跑了一遍 Bfs,这里我们就在分割线上每个格点都跑一边 Dijkstra(带权图)。

    还是和上面一样,我们可以通过这一条线上所有的最短路长结果更新该区域内的询问,其中所有跨越分割线的询问的答案就已经得到了。

    至于两端同在一遍的询问,我们分治下去就行。

    为了像上面一样,将平面尽可能分的均匀,我们选取矩形的长边,然后取中点分割。

    这个复杂度分析有点麻烦,是 (O(S^{1.5}log S)) 的((S) 为网格图面积)。具体证(kou)明(hu):

    由于我们总是按长边分割,那么显然当 (n=m)(正方形)时复杂度最高。设 (T(n, Q)) 为面积为 (S) 的矩形计算的复杂度。那么有:(T(S) = S^{1.5}log_2 S + 2T(frac S 2))。其中 (S^{1.5}log_2 S) 说明会做不超过 (sqrt{S}) 次 Dijkstra 算法。

    然后我们展开,得:

    [egin{aligned} T(S) & = S^{1.5}log_2 S + frac{S^{1.5}}{2}log_2left(frac S 2 ight) + 4Tleft(frac S 4 ight)\ & = left(1+frac{1}{2} ight)S^{1.5}log_2 S - frac S 2 + 4Tleft(frac S 4 ight)\ & = left(sumlimits_{i=0}^{log_2 S} frac{1}{2^i} ight)S^{1.5}log_2 S - left(sumlimits_{i=1}^{log_2 S} frac{1}{2^i} ight)S \ & = O(S^{1.5}log S) end{aligned} ]

    小结

    其实还是上面那个套路。只不过变成了网格图更加复杂了,但主要思想没有变:在分割线上枚举 中转点

    我们发现网格图比上一题的多边形貌似更好分割一些,我们不用遍历一圈点找到分割位置,只要 ( exttt{/ 2}) 就行了。

    为什么这么说呢?因为下次碰到 网格图多次询问最短路 我们也可以考虑这样的方法。

    一道毒瘤题

    题目简述

    【CodeChef QGRID】Querying on a Grid

    给定一个网格图,(m)(n) 列。格点上有点权,初始为 (0);连接格点的边有边权,初始给定。

    先有 (Q) 次操作:

    • 1 i1 j1 i2 j2 c 在格点 ((i_1, j_1))((i_2, j_2)) 的最短路径(保证唯一,最短指路径上的边权和最小)上的每个格点的点权加上 (c)
    • 2 i j 询问格点 ((i, j)) 的点权是多少。

    (1le mle 3, 1le nle 10^5, 1le Qle 10^5)

    分析

    老套路,看见网格图最短路尝试分治。所幸这里 (mle 3) 那么只要竖着切就行了。

    但是这题我们不止需要最短路长,还要知道整条最短路径。于是我们就把最短路径树给一并搞出来。注意这里我们需要将每层分治得到的最短路径树都存下来。可以发现我们实体化了这个分治过程,这样可以更简单地应付又有修改又有查询的情况。

    然后原修改可以转化为在最短路径树上的链修改,而单点询问也可以转化为树上的点询问。然而别忘了对每一层的树都要修改。

    这样我们一次修改 (O(mlog n)) 棵最短路树,每次修改我们只要 (O(log (nm))) 的时间。于是时空复杂度为 (O((n+Q)nlog nlog(nm)))代码巨难写写了一下午 常数巨大跑了 4s

    小结

    上述分治过程的 实体化 可以实现在线处理这些询问,对前面两题理论上也可以用一样的套路。不过空间复杂度和时空常数都很大,因此能离线就离线吧。

    总结

    好吧这篇文章确实是水的

    不过总结几个套路还是挺有必要的,希望下次做这种题还能有一点思路。

  • 相关阅读:
    OpenStreetMap、googleMap等经纬度和行列号之间相互转化(python,JavaScript,php,Java,C#等)
    利用whoosh对mongoDB的中文文档建立全文检索
    js前端读写文件的方法(json、excel)
    部分网站公开数据的汇总(1)
    部分网站公开数据的汇总(2)
    Buuctf-misc-[BJDCTF 2nd]最简单的misc-y1ng
    Buuctf-web-[SUCTF 2019]CheckIn
    Buuctf-web-[ACTF2020 新生赛]Upload
    Buuctf-web-[ACTF2020 新生赛]BackupFile
    Buuctf-web-[极客大挑战 2019]Upload
  • 原文地址:https://www.cnblogs.com/-Wallace-/p/13752810.html
Copyright © 2011-2022 走看看