\(\mathcal{Description}\)

  Link.

  给定一个 \(n\) 个点 \(m\) 条边的仙人掌,\(q\) 组询问两点最短路。

  \(n,q\le10^4\),\(m\le2\times10^4\)。

\(\mathcal{Solution}\)

  提出一个环来考虑,从环上一点 \(u\) 到 \(v\),无非两条路径。可以按顺序处理一个前缀和。如图:

  令 \(sum_2\) 为结点 \(1\) 到 \(2\) 的顺时针距离,\(sum_3\) 为结点 \(1\) 到 \(3\) 的顺时针距离……特别地,\(sum_1\) 记录整个环的大小。那么环上 \(u\) 和 \(v\) 的最短距离就是 \(\min\{|sum_u-sum_v|,sum_1-|sum_u-sum_v|\}\)。这样就能 \(\mathcal O(1)\) 求到了。

  接下来建圆方树(很多题解说建树的细节与普通图不一样,其实正常建也没有任何问题 qwq),发现一个圆点走进方点(点双),在走到父亲圆点的最短距离可以在 \(\text{Tarjan}\) 算法中预处理出来。考虑圆方树上的边权:圆点到父亲方点的边权为上述最短距离,否则为 \(0\)。对于询问 \((u,v)\),找到其 \(\text{LCA}\)(设为 \(w\)),分 \(w\) 的情况讨论:

  • \(w\) 是圆点,那么 \(\text{LCA}\) 时求出的树上距离就是答案。
  • \(w\) 是方点,此时树上距离实际上是 \(u\) 和 \(v\) 走到 \(w\) 的父亲圆点的距离之和,明显时错误的——\(u\) 和 \(v\) 走进同一个点双后,需要求一次最短距离而非直接在父亲会和。所以可以倍增求到 \(u\) 走进点双的第一个点(即方点向 \(u\) 点方向的儿子圆点)\(u'\),同理求出 \(v'\),利用处理的前缀和求到 \(u'\) 到 \(v'\) 的最短距离。那么答案就是 \(\operatorname{dist}(u,u')+\operatorname{dist}(u',v')+\operatorname{dist}(v',v)\)。

  复杂度 \(\mathcal O(n\log n)\)。不过这里作者偷懒用 std::map 记录了每个环的 \(sum\),写得好看一点是可以在这一部分做到 \(\mathcal O(n)\)。但求走进点双的第一个点似乎必须用带 \(\log\) 的算法 owo?

\(\mathcal{Code}\)

  1. #include <map>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #define adj( g, u, v, c ) \
  5. for ( int eid = g.head[u], v, c; v = g.to[eid], c = g.cst[eid], eid; eid = g.nxt[eid] )
  6. const int MAXN = 2e4, MAXM = 4e4;
  7. int n, m, q, snode, fa[MAXN + 5][15];
  8. int dfc, top, dfn[MAXN + 5], low[MAXN + 5], stk[MAXN + 5];
  9. int dep[MAXN + 5], dis[MAXN + 5], root[MAXN + 5];
  10. std::map<int, int> sum[MAXN + 5];
  11. struct Graph {
  12. int ecnt, head[MAXN + 5], to[MAXM + 5], cst[MAXM + 5], nxt[MAXM + 5];
  13. inline void link ( const int s, const int t, const int c ) {
  14. to[++ ecnt] = t, cst[ecnt] = c, nxt[ecnt] = head[s];
  15. head[s] = ecnt;
  16. }
  17. inline void add ( const int u, const int v, const int c ) {
  18. link ( u, v, c ), link ( v, u, c );
  19. }
  20. } src, tre;
  21. inline char fgc () {
  22. static char buf[1 << 17], *p = buf, *q = buf;
  23. return p == q && ( q = buf + fread ( p = buf, 1, 1 << 17, stdin ), p == q ) ? EOF : *p ++;
  24. }
  25. inline int rint () {
  26. int x = 0; char s = fgc ();
  27. for ( ; s < '0' || '9' < s; s = fgc () );
  28. for ( ; '0' <= s && s <= '9'; s = fgc () ) x = x * 10 + ( s ^ '0' );
  29. return x;
  30. }
  31. inline void wint ( const int x ) {
  32. if ( 9 < x ) wint ( x / 10 );
  33. putchar ( x % 10 ^ '0' );
  34. }
  35. inline int min_ ( const int a, const int b ) { return a < b ? a : b; }
  36. inline bool chkmin ( int& a, const int b ) { return b < a ? a = b, true : false; }
  37. inline bool chkmax ( int& a, const int b ) { return a < b ? a = b, true : false; }
  38. inline int cost ( const Graph& g, const int s, const int t ) {
  39. adj ( g, s, u, c ) if ( u == t ) return c;
  40. return -1;
  41. }
  42. inline int mncost ( const int id, int u, int v ) {
  43. int t1 = sum[id][u], t2 = sum[id][v];
  44. if ( t1 > t2 ) t1 ^= t2 ^= t1 ^= t2;
  45. return min_ ( t2 - t1, sum[id][root[id]] - ( t2 - t1 ) );
  46. }
  47. inline void buildSquare ( const int rt, const int p, const int sid ) {
  48. int beg = top; for ( ; stk[beg] ^ p; -- beg );
  49. root[sid] = rt;
  50. // loop: rt - stk[beg] - stk[beg + 1] - ... - stk[top] - rt.
  51. for ( int i = beg, cur = p, pre = rt; i <= top; pre = cur, cur = stk[++ i] ) {
  52. sum[sid][cur] = sum[sid][pre] + cost ( src, pre, cur );
  53. }
  54. sum[sid][rt] = sum[sid][stk[top]] + cost ( src, stk[top], rt );
  55. int u;
  56. do {
  57. u = stk[top --];
  58. tre.add ( sid, u, mncost ( sid, rt, u ) );
  59. } while ( u ^ p );
  60. }
  61. inline void Tarjan ( const int u, const int fa ) {
  62. dfn[u] = low[u] = ++ dfc, stk[++ top] = u;
  63. adj ( src, u, v, c ) if ( v ^ fa ) {
  64. if ( ! dfn[v] ) {
  65. Tarjan ( v, u ), chkmin ( low[u], low[v] );
  66. if ( low[v] >= dfn[u] ) {
  67. tre.add ( u, ++ snode, 0 );
  68. buildSquare ( u, v, snode );
  69. }
  70. } else chkmin ( low[u], dfn[v] );
  71. }
  72. }
  73. inline void init ( const int u, const int f ) {
  74. dep[u] = dep[fa[u][0] = f] + 1;
  75. for ( int i = 1; i <= 14; ++ i ) fa[u][i] = fa[fa[u][i - 1]][i - 1];
  76. adj ( tre, u, v, c ) if ( v ^ f ) {
  77. dis[v] = dis[u] + c;
  78. init ( v, u );
  79. }
  80. }
  81. inline int calcLCA ( int u, int v ) {
  82. if ( dep[u] < dep[v] ) u ^= v ^= u ^= v;
  83. for ( int i = 14; ~ i; -- i ) if ( dep[fa[u][i]] >= dep[v] ) u = fa[u][i];
  84. if ( u == v ) return u;
  85. for ( int i = 14; ~ i; -- i ) if ( fa[u][i] ^ fa[v][i] ) u = fa[u][i], v = fa[v][i];
  86. return fa[u][0];
  87. }
  88. inline int climb ( int u, const int tar ) {
  89. for ( int i = 14; ~ i; -- i ) if ( dep[fa[u][i]] > dep[tar] ) u = fa[u][i];
  90. return u;
  91. }
  92. int main () {
  93. n = snode = rint (), m = rint (), q = rint ();
  94. for ( int i = 1, u, v, w; i <= m; ++ i ) {
  95. u = rint (), v = rint (), w = rint ();
  96. src.add ( u, v, w );
  97. }
  98. Tarjan ( 1, 0 );
  99. init ( 1, 0 );
  100. for ( int u, v; q --; ) {
  101. u = rint (), v = rint ();
  102. int w = calcLCA ( u, v );
  103. if ( w <= n ) wint ( dis[u] + dis[v] - 2 * dis[w] );
  104. else {
  105. int pu = climb ( u, w ), pv = climb ( v, w );
  106. wint ( dis[u] - dis[pu] + dis[v] - dis[pv] + mncost ( w, pu, pv ) );
  107. }
  108. putchar ( '\n' );
  109. }
  110. return 0;
  111. }

Solution -「洛谷 P5236」「模板」静态仙人掌的更多相关文章

  1. 「区间DP」「洛谷P1043」数字游戏

    「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...

  2. 「 洛谷 」P2768 珍珠项链

    珍珠项链 题目限制 内存限制:125.00MB 时间限制:1.00s 标准输入输出 题目知识点 动态规划 \(dp\) 矩阵 矩阵乘法 矩阵加速 矩阵快速幂 题目来源 「 洛谷 」P2768 珍珠项链 ...

  3. 「 洛谷 」P4539 [SCOI2006]zh_tree

    小兔的话 推荐 小兔的CSDN [SCOI2006]zh_tree 题目限制 内存限制:250.00MB 时间限制:1.00s 标准输入输出 题目知识点 思维 动态规划 \(dp\) 区间\(dp\) ...

  4. 「 洛谷 」P2151 [SDOI2009]HH去散步

    小兔的话 欢迎大家在评论区留言哦~ HH去散步 题目限制 内存限制:125.00MB 时间限制:1.00s 标准输入 标准输出 题目知识点 动态规划 \(dp\) 矩阵 矩阵乘法 矩阵加速 矩阵快速幂 ...

  5. 【洛谷P3369】【模板】普通平衡树题解

    [洛谷P3369][模板]普通平衡树题解 题目链接 题意: 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3 ...

  6. Solution -「CTS 2019」「洛谷 P5404」氪金手游

    \(\mathcal{Description}\)   Link.   有 \(n\) 张卡牌,第 \(i\) 张的权值 \(w_i\in\{1,2,3\}\),且取值为 \(k\) 的概率正比于 \ ...

  7. Solution -「JSOI 2019」「洛谷 P5334」节日庆典

    \(\mathscr{Description}\)   Link.   给定字符串 \(S\),求 \(S\) 的每个前缀的最小表示法起始下标(若有多个,取最小的).   \(|S|\le3\time ...

  8. Solution -「洛谷 P4372」Out of Sorts P

    \(\mathcal{Description}\)   OurOJ & 洛谷 P4372(几乎一致)   设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...

  9. Solution -「POI 2010」「洛谷 P3511」MOS-Bridges

    \(\mathcal{Description}\)   Link.(洛谷上这翻译真的一言难尽呐.   给定一个 \(n\) 个点 \(m\) 条边的无向图,一条边 \((u,v,a,b)\) 表示从 ...

随机推荐

  1. RabbitMQ --- 直连交换机 【 有回调方法,获取消费结果 】

    1.前言 上一随笔详细记录了直连交换机的方法,发送的消息是异步的,如果消息未被消费者消费,那么可以一直存在消息队列中. 那么有没有办法做一个回调,当消息被消费后,被通知消息成功被消费者消费啦? 答案是 ...

  2. javascript中new url()属性,轻松解析url地址

    1.首先写一个假的地址(q=URLUtils.searchParams&topic=api)相当于当前的window.location.href const urlParams = new U ...

  3. linux获取 GPG 密钥失败

    实质性问题就是自己系统没有yum的GPG密钥 查看自己系统版本 cat /etc/issue 登陆mirrors.163.com 找到自己系统对应的密钥  RPM-GPG-KEY-CentOS-3   ...

  4. test_3 简单密码破解

    题目描述:密码是我们生活中非常重要的东东,我们的那么一点不能说的秘密就全靠它了.哇哈哈. 接下来渊子要在密码之上再加一套密码,虽然简单但也安全. 假设渊子原来一个BBS上的密码为zvbo9441987 ...

  5. 【Android】安卓开发中的布局与事件

    [Android]安卓开发中的布局与事件 1.Android Studio下载配置 非常简单的百度然后点击下载安装就好了.注意的是,本来我是打算使用评价还不错的Genymotion这个软件来充当虚拟机 ...

  6. 【刷题-LeetCode】304. Range Sum Query 2D - Immutable

    Range Sum Query 2D - Immutable Given a 2D matrix matrix, find the sum of the elements inside the rec ...

  7. MySQL之MVCC与幻读

    转自 https://blog.csdn.net/qq_31930499/article/details/110393988 如果是快照度,直接采用MVCC,如果是当前读,才会走next-key lo ...

  8. context包

    目录 Context包到底是干嘛用的? context原理 什么时候应该使用 Context? 如何创建 Context? 主协程通知有子协程,子协程又有多个子协程 context核心接口 empty ...

  9. Kubernetes:故障排查(Trouble Shooting)方法总结

    Blog:博客园 个人 本文部分内容源自网络,侵删. 概述 为了跟踪和发现在Kubernetes集群中运行的容器应用出现的问题,我们常用如下排查方法: 查看Kubernetes对象的当前运行时信息,特 ...

  10. 字节跳动的一道python面试题

    #!/usr/bin/python #coding=utf-8 #好好学习,天天向上 lst = ['hongkong','xiantyk','chinat','guangdong','z'] lst ...