今天又在群里看到一个同学问$n$个$n$条边,怎么查询两点直接最短路。看来这种题还挺常见的。
为什么最终答案要从42个点的最短路(到$x,y$)之和,还有$x,y$到$LCA(x,y)$的距离里面取呢?
就是如果走非树边,那么一定要走42个点中的一个,不走树边,就是LCA求了。
我写的太蠢了,还写生成树,看大家都是LCA中的dfs直接标记下就行了。
验证了算法的正确,我又试了试把每条非树边只加一个点,也是AC的,其实想了想,确实正确。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long ll; 5 const int maxn = 1e5 + 5; 6 const ll INF = 0x3f3f3f3f3f3f3f3f; 7 struct edge{ 8 int to; 9 ll cost; 10 friend bool operator < (edge A,edge B){ 11 return A.cost > B.cost; 12 } 13 }; 14 int u[maxn], v[maxn], w[maxn], vis[maxn]; 15 vector<int> flag; 16 ll d[43][maxn]; 17 vector<edge> g[maxn]; 18 bool done[maxn]; 19 void dijkstra(ll d[], vector<edge> g[], bool done[], int s){ 20 fill(d,d + maxn, INF); 21 fill(done, done + maxn, false); 22 d[s] = 0; 23 priority_queue<edge> q; 24 q.push((edge){s,0}); 25 while(!q.empty()){ 26 edge cur = q.top(); q.pop(); 27 int v = cur.to; 28 if(done[v]) continue; 29 done[v] = true; 30 for(int i = 0; i<g[v].size(); i++){ 31 edge e = g[v][i]; 32 if(d[e.to]>d[v]+e.cost){ 33 d[e.to] = d[v]+e.cost; 34 q.push((edge){e.to,d[e.to]}); 35 } 36 } 37 } 38 } 39 ///加边 40 int cnt, h[maxn]; 41 struct node 42 { 43 int to, pre, v; 44 } e[maxn << 1]; 45 void init() 46 { 47 cnt = 0; 48 memset(h, 0, sizeof(h)); 49 } 50 void add(int from, int to, int v) 51 { 52 cnt++; 53 e[cnt].pre = h[from]; ///5-->3-->1-->0 54 e[cnt].to = to; 55 e[cnt].v = v; 56 h[from] = cnt; 57 } 58 ///LCA 59 ll dist[maxn]; 60 int dep[maxn]; 61 int anc[maxn][33]; ///2分的父亲节点 62 void dfs(int u, int fa) 63 { 64 for(int i = h[u]; i; i = e[i].pre) 65 { 66 int v = e[i].to; 67 if(v == fa) continue; 68 dist[v] = dist[u] + e[i].v; 69 dep[v] = dep[u] + 1; 70 anc[v][0] = u; 71 dfs(v, u); 72 } 73 } 74 void LCA_init(int n) 75 { 76 for(int j = 1; (1 << j) < n; j++) 77 for(int i = 1; i <= n; i++) if(anc[i][j-1]) 78 anc[i][j] = anc[anc[i][j-1]][j-1]; 79 } 80 int LCA(int u, int v) 81 { 82 int log; 83 if(dep[u] < dep[v]) swap(u, v); 84 for(log = 0; (1 << log) < dep[u]; log++); 85 for(int i = log; i >= 0; i--) 86 if(dep[u] - (1 << i) >= dep[v]) u = anc[u][i]; 87 if(u == v) return u; 88 for(int i = log; i >= 0; i--) 89 if(anc[u][i] && anc[u][i] != anc[v][i]) 90 u = anc[u][i], v = anc[v][i]; 91 return anc[u][0]; 92 } 93 94 int pre[maxn]; 95 int Find(int x) 96 { 97 if(x == pre[x]) return x; 98 else return pre[x] = Find(pre[x]); 99 } 100 int main() 101 { 102 int n, m; scanf("%d %d", &n, &m); 103 for(int i = 1; i <= m; i++) 104 { 105 scanf("%d %d %d", &u[i], &v[i], &w[i]); 106 g[u[i]].push_back({v[i], w[i]}); 107 g[v[i]].push_back({u[i], w[i]}); 108 } 109 ///先找生成树 110 for(int i = 1; i <= n; i++) pre[i] = i; 111 for(int i = 1; i <= m; i++) 112 { 113 int x = Find(u[i]); 114 int y = Find(v[i]); 115 if(x != y) 116 { 117 pre[x] = y; 118 } 119 else flag.push_back(u[i]), flag.push_back(v[i]), vis[i] = 1; 120 } 121 ///对至多42个点跑最短路 122 sort(flag.begin(), flag.end()); 123 flag.erase(unique(flag.begin(), flag.end()), flag.end()); 124 for(int i = 0; i < flag.size(); i++) 125 { 126 dijkstra(d[i], g, done, flag[i]); 127 } 128 ///跑LCA 129 init(); 130 for(int i = 1; i <= m; i++) 131 { 132 if(!vis[i]) add(u[i], v[i], w[i]), add(v[i], u[i], w[i]); 133 } 134 dist[1] = 0; 135 dfs(1, 0); 136 LCA_init(n); 137 int Q; scanf("%d", &Q); 138 ///查询 139 while(Q--) 140 { 141 int x, y; scanf("%d %d", &x, &y); 142 ll ans = dist[x] + dist[y] - 2LL * dist[LCA(x, y)]; 143 for(int i = 0; i < flag.size(); i++) 144 { 145 ans = min(ans, d[i][x] + d[i][y]); 146 } 147 printf("%lld\n", ans); 148 } 149 return 0; 150 }
原文地址:https://www.cnblogs.com/wangwangyu/p/9693457.html
时间: 2024-10-09 17:25:18