博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[BZOJ2125]最短路
阅读量:4687 次
发布时间:2019-06-09

本文共 4412 字,大约阅读时间需要 14 分钟。

2125: 最短路

Time Limit: 1 Sec  Memory Limit: 259 MB Submit: 985  Solved: 411 [][][]

Description

给一个N个点M条边的连通无向图,满足每条边最多属于一个环,有Q组询问,每次询问两点之间的最短路径。

Input

输入的第一行包含三个整数,分别表示N和M和Q 下接M行,每行三个整数v,u,w表示一条无向边v-u,长度为w 最后Q行,每行两个整数v,u表示一组询问

Output

输出Q行,每行一个整数表示询问的答案

Sample Input

9 10 2
1 2 1
1 4 1
3 4 1
2 3 1
3 7 1
7 8 2
7 9 2
1 5 3
1 6 4
5 6 1
1 9
5 7

Sample Output

5
6

HINT

对于100%的数据,N<=10000,Q<=10000

 

学习了一波圆方树,大概做法就是把在原仙人掌上的点设为圆点,然后对于每个环,把这个环上除了环顶的所有点重新连在一个方点下面,边权为到环顶的最短距离,方点连在环顶下,边权为0,其它的点该怎么连就怎么连,可以证明这样构成的树是和原仙人掌等价的

那么求两点最短距离就可以搬用树上两点距离的做法,先求出lca。如果lca是圆点,那么直接就是路径上边权和,如果lca是方点,路径肯定还包含了环上的一段,那么就找到lca下方的两个点,根据环的总长度以及两个点分别到环顶的距离和最短距离方向可算出环上的一段

求环用Tarjan的点双算法,求lca用的倍增,感觉这样好找lca下方的两点

#include 
#include
using namespace std;char buf[10000000], *ptr = buf - 1;inline int readint(){ int n = 0; char ch = *++ptr; while(ch < '0' || ch > '9') ch = *++ptr; while(ch <= '9' && ch >= '0'){ n = (n << 1) + (n << 3) + ch - '0'; ch = *++ptr; } return n;}const int maxn = 20000 + 10, maxm = 50000 + 10;struct Edge{ int to, val, next; Edge(){} Edge(int _t, int _v, int _n){ to = _t; val = _v; next = _n; }}e[maxm];int fir[maxn] = {
0}, Fir[maxn] = {
0}, cnt = 1;inline void ins(int u, int v, int w){ e[++cnt] = Edge(v, w, fir[u]); fir[u] = cnt; e[++cnt] = Edge(u, w, fir[v]); fir[v] = cnt;}inline void link(int u, int v, int w){ e[++cnt] = Edge(v, w, Fir[u]); Fir[u] = cnt;}int n, m, q, N;int dfn[maxn] = {
0}, low[maxn], Index = 0;int sta[maxn], top = 0;int tmp[maxn], dir[maxn], val[maxn], tot[maxn];void tarjan(int u, int f){ dfn[u] = low[u] = ++Index; sta[++top] = u; for(int v, i = fir[u]; i; i = e[i].next){ v = e[i].to; if(!dfn[v]){ val[v] = e[i].val; tarjan(v, i ^ 1); low[u] = min(low[u], low[v]); if(low[v] > dfn[u]){ link(u, v, e[i].val); top--; } else if(low[v] == dfn[u]){ link(u, ++N, 0); int c = 0, ss; do{ tmp[++c] = sta[top]; }while(sta[top--] != v); for(int j = fir[tmp[1]]; j; j = e[j].next) if(e[j].to == u){ ss = tot[N] = e[j].val; break; } for(int j = 1; j <= c; j++) tot[N] += val[tmp[j]]; for(int j = 1; j <= c; j++){ if(ss < tot[N] - ss){ link(N, tmp[j], ss); dir[tmp[j]] = 1; } else{ link(N, tmp[j], tot[N] - ss); dir[tmp[j]] = 2; } ss += val[tmp[j]]; } } } else if(i ^ f) low[u] = min(low[u], dfn[v]); }}int dep[maxn], fa[maxn][16], road[maxn][16];void dfs(int u){ for(int t = 1; (1 << t) <= dep[u]; t++){ fa[u][t] = fa[fa[u][t - 1]][t - 1]; road[u][t] = road[u][t - 1] + road[fa[u][t - 1]][t - 1]; } for(int v, i = fir[u]; i; i = e[i].next){ v = e[i].to; fa[v][0] = u; road[v][0] = e[i].val; dep[v] = dep[u] + 1; dfs(v); }}inline int Query(){ int x = readint(); int y = readint(); if(dep[x] < dep[y]) swap(x, y); int d = dep[x] - dep[y], ans = 0; for(int t = 0; t < 16; t++) if(d & (1 << t)){ ans += road[x][t]; x = fa[x][t]; } if(x == y) return ans; for(int t = 15; ~t; t--) if(fa[x][t] != fa[y][t]){ ans += road[x][t] + road[y][t]; x = fa[x][t]; y = fa[y][t]; } if(fa[x][0] <= n) return ans + road[x][0] + road[y][0]; else if(dir[x] == dir[y]) return ans + abs(road[x][0] - road[y][0]); else return ans + min(road[x][0] + road[y][0], tot[fa[x][0]] - road[x][0] - road[y][0]);}int main(){ fread(buf, sizeof(char), sizeof(buf), stdin); N = n = readint(); m = readint(); q = readint(); for(int u, v, w, i = 1; i <= m; i++){ u = readint(); v = readint(); w = readint(); ins(u, v, w); } tarjan(1, 0); for(int i = 1; i <= N; i++) fir[i] = Fir[i]; dep[1] = 0; dfs(1); for(int i = 1; i <= q; i++) printf("%d\n", Query()); return 0;}

 

转载于:https://www.cnblogs.com/ruoruoruo/p/7445303.html

你可能感兴趣的文章
你懂AI吗(1)
查看>>
双拼输入法
查看>>
CentOS7 中防火墙配置
查看>>
php扩展目录
查看>>
PageRank算法
查看>>
git的介绍和配置
查看>>
C 语言 习题 1-14
查看>>
密码锁
查看>>
值类型和引用类型区别,一看就懂
查看>>
UVa 11375 Matches
查看>>
JdbcTemplate
查看>>
leetcode 2. 两数相加(Add Two Numbers)
查看>>
Crimm Imageshop 2.0 发布。
查看>>
Xcode中修改默认文件头部注释
查看>>
从一个针对ASP.NET MVC框架的Controller.Action的请求处理顺序来说整个请求过程。
查看>>
pandas read excel文件碰到的一个小问题
查看>>
教师发表职称论文须注意事项
查看>>
libGDX简介
查看>>
《深入理解计算机系统(第三版)》第二章学习总结
查看>>
JavaScript专题——专题三 JavaScript 面向对象
查看>>