简单的开头
“分治法”和“最短路”两者似乎是毫不相干的东西,然而就是有毒瘤出题人将这两个整在了一起。
但其实这还算是一类比较有意思的问题,于是就有了这一篇水文。
一道简单题
题目简述
【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
小结
上述分治过程的 实体化 可以实现在线处理这些询问,对前面两题理论上也可以用一样的套路。不过空间复杂度和时空常数都很大,因此能离线就离线吧。
总结
好吧这篇文章确实是水的
不过总结几个套路还是挺有必要的,希望下次做这种题还能有一点思路。