
【模板】静态仙人掌
询问有两个,分别是询问 1 → 9 1\rightarrow 9 1→9 和 5 → 7 5\rightarrow 7 5→7 的最短路 显然答案分别为 5 5 5 和 6 6 6 。
我们就把那些方点走到圆点的长度设为它到它父亲的最短的距离,找环用 tarjan 来求,圆点和圆点直接的边就不变。 然后对于求 x x x 到 y y y 的最短路,我们先求出他们的 lca ,再分开讨论:
发布日期:2021-05-07 06:56:43
浏览次数:26
分类:精选文章
本文共 4299 字,大约阅读时间需要 14 分钟。
【模板】静态仙人掌 \operatorname{【模板】静态仙人掌} 【模板】静态仙人掌
题目链接:
题目背景
这是一道静态仙人掌 (Block Forest Data Structure) 的模板题。
如果您不知道什么是仙人掌,那么此处给出无向仙人掌图的定义:任意一条边至多只出现在一条简单回路的无向连通图称为仙人掌。
题目
给你一个有 n n n 个点和 m m m 条边的仙人掌图,和 q q q 组询问
每次询问两个点 u , v u,v u,v ,求两点之间的最短路。输入
第一行三个正整数 n , m , q n,m,q n,m,q ,意义如题目描述。
接下来 m m m 行,每行三个正整数 u , v , w u,v,w u,v,w ,表示 u , v u,v u,v 之间有一条权值为 w w w 的无向边。 然后 q q q 行,每行两个正整数 u , v u,v u,v ,询问 u u u 到 v v v 的最短路。输出
q q q 行,每行一个正整数,对应一次询问的结果。
样例输入1
9 10 21 2 11 4 13 4 12 3 13 7 17 8 27 9 21 5 31 6 45 6 11 95 7
样例输出1
56
样例解释
样例 1 1 1 中的仙人掌是这个样子的:

样例输入2
9 10 31 2 12 3 12 4 43 4 24 5 15 6 16 7 27 8 28 9 45 9 21 95 83 4
样例输出2
752
数据范围
1 ≤ n , q ≤ 10000 1≤n,q≤10000 1≤n,q≤10000
1 ≤ m ≤ 20000 1\le m \le 20000 1≤m≤20000 1 ≤ w ≤ 1 0 5 1\le w \le 10^5 1≤w≤105提示
请注意时限为 300 ms 300\text{ms} 300ms
思路
这道题是一个叫做圆方树的东西。
假设图示这样的,那我们把原来的图里面的点叫做圆点,如果图中有环,就建一个方点连向环的各个点,原来的这个环的边就抹掉。就像这样:

- lca 是圆点,就是普通的两点距离。
- lca 是方点,就找到 lca 的儿子中分别是 x x x 和 y y y 的祖先的那两个点 X , Y X,Y X,Y ,那答案就是两个儿子分别到他们祖先的距离之和再加上他们祖先的最短距离(因为两祖先在一个环内,两条路最短的那条就是了),就是答案。
要开 l o n g l o n g long\ long long long ,好像不用快读快输但是我用了。
代码
#include#include #include #define ll long longusing namespace std;struct node { ll x, to, nxt;}e[400001], ee[400001];ll n, m, q, x, y, z, w, ans;ll nn, KK, KKK, lca, X, Y;ll l[100001], fa[200001], dep[200001], dis[200001], diss[200001];ll dfn[200001], low[200001], le[200001], lee[200010], fath[21][200001];ll read() { //快读 ll an = 0, zhengfu = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') zhengfu = -zhengfu; c = getchar(); } while (c >= '0' && c <= '9') { an = an * 10 + c - '0'; c = getchar(); } return an * zhengfu;}void add0(ll x, ll y, ll z) { //建原图 e[++KK] = (node){ z, y, le[x]}; le[x] = KK; e[++KK] = (node){ z, x, le[y]}; le[y] = KK;}void add(ll x, ll y, ll z) { //建圆方树的图 ee[++KKK] = (node){ z, y, lee[x]}; lee[x] = KKK; ee[++KKK] = (node){ z, x, lee[y]}; lee[y] = KKK;}void build(ll x, ll y, ll lon) { //建方点 nn++; ll len = lon; for (ll i = y; i != fa[x]; i = fa[i]) { diss[i] = len; len += l[i]; } diss[nn] = diss[x]; diss[x] = 0; for (ll i = y; i != fa[x]; i = fa[i]) { len = min(diss[i], diss[nn] - diss[i]); add(i, nn, len); }}void dfs1(ll now) { //tarjan dfn[now] = low[now] = ++w; for (ll i = le[now]; i; i = e[i].nxt) if (e[i].to != fa[now]) { if (!dfn[e[i].to]) { fa[e[i].to] = now; l[e[i].to] = e[i].x; dfs1(e[i].to); low[now] = min(low[now], low[e[i].to]); } else low[now] = min(low[now], dfn[e[i].to]); if (low[e[i].to] > dfn[now]) add(now, e[i].to, e[i].x);//圆点和圆点连边 } for (ll i = le[now]; i; i = e[i].nxt) if (fa[e[i].to] != now && dfn[now] < dfn[e[i].to]) build(now, e[i].to, e[i].x);//找到环建方点}void dfs2(ll now) { //遍历来预处理 dep[now] = dep[fath[0][now]] + 1; for (int i = 1; i <= 20; i++) fath[i][now] = fath[i - 1][fath[i - 1][now]]; for (ll i = lee[now]; i; i = ee[i].nxt) if (ee[i].to != fath[0][now]) { fath[0][ee[i].to] = now; if (!dis[ee[i].to]) dis[ee[i].to] = dis[now] + ee[i].x; else dis[ee[i].to] = min(dis[ee[i].to], dis[now] + ee[i].x); dfs2(ee[i].to); }}ll LCA(ll x, ll y) { //求lca if (dep[x] < dep[y]) swap(x, y); for (ll i = 20; i >= 0; i--) if (dep[fath[i][x]] >= dep[y]) x = fath[i][x]; for (ll i = 20; i >= 0; i--) if (fath[i][x] != fath[i][y]) { x = fath[i][x]; y = fath[i][y]; } X = x; Y = y; if (x == y) return x; return fath[0][x];}void writec(ll x) { //快输 if (x > 9) writec(x / 10); putchar(x % 10 + '0');}void write(ll x) { if (x < 0) writec(-x); writec(x); putchar('\n');}int main() { n = read();//读入 m = read(); q = read(); nn = n; for (ll i = 1; i <= m; i++) { x = read();//读入 y = read(); z = read(); add0(x, y, z);//建原来的图 } dfs1(1);//建圆方树 fath[0][1] = 1; dfs2(1);//初始化 for (ll i = 1; i <= q; i++) { x = read();//读入 y = read(); lca = LCA(x, y);//求出lca if (lca > n) { //lca在方点 ans = dis[x] - dis[X] + dis[y] - dis[Y]; ll far = abs(diss[X] - diss[Y]); ans = ans + min(far, diss[lca] - far); } else ans = dis[x] - dis[lca] + dis[y] - dis[lca];//在圆点 write(ans);//输出 } return 0;}
发表评论
最新留言
路过,博主的博客真漂亮。。
[***.116.15.85]2025年04月15日 08时47分00秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Python 面向对象进阶
2019-03-06
Linux常用统计命令之wc
2019-03-06
Git安装及使用以及连接GitHub方法详解
2019-03-06
docker容器与虚拟机的区别
2021-05-09
shell脚本里使用echo输出颜色
2021-05-09
Python2跟Python3的区别
2021-05-09
并发编程——IO模型详解
2019-03-06
Java之封装,继承,多态
2019-03-06
wait()与notify()
2019-03-06
使用js打印时去除页眉页脚
2019-03-06
Spring security OAuth2.0认证授权学习第二天(基础概念-RBAC)
2019-03-06
ORA-00904: "FILED_TYPE": 标识符无效
2019-03-06
Redis系统学习之Redis性能测试工具
2019-03-06
数据仓库系列之维度建模
2019-03-06
Scala教程之:函数式的Scala
2019-03-06
java中DelayQueue的使用
2019-03-06
java程序员从小工到专家成神之路(2020版)-持续更新中,附详细文章教程
2019-03-06
线程stop和Interrupt
2019-03-06
Android中定时执行任务的3种实现方法
2019-03-06
nodejs中npm常用命令
2019-03-06