ps:非常非常重要的一个条件 m - n <= 20,考虑图的生成树,那么(u,v)之间的最短路上的边有两种情况,一是最短路不经过非生成树的边,二是经过非生成树的边。又非生成树的边只有20条,则连接的点最多有40个,所以对这40个点分别跑一次DJS,然后取最小值就行了。(换言之:如果最短路经过非生成树边,那么我们就可以枚举这些边)
const int N = 100005; int n, m, tot, top; int head[N], pa[N][20], dp[N], tp[100]; LL d[N][55]; bool use[N]; struct node { int to, va, next; } e[N * 2]; priority_queue<P, vector<P>, greater<P> > q; void Inite() { top = tot = 0; mem(head, -1); Rep(i, 1, n) Rep(j, 1, 50) d[i][j] = INF64; } void addedge(int u, int v, int w) { e[tot].to = v, e[tot].va = w, e[tot].next = head[u], head[u] = tot++; } void DFS(int u, int p) { use[u] = 1; pa[u][0] = p; for (int i = head[u]; ~i; i = e[i].next) if (e[i].to != p) { int v = e[i].to; if (use[v]) tp[++top] = v, tp[++top] = u; else { dp[v] = dp[u] + 1; d[v][0] = d[u][0] + e[i].va; DFS(v, u); } } } int Lca(int u, int v) { if (u == v) return u; if (dp[u] > dp[v]) swap(u, v); for (int i = 19; ~i; --i) if (dp[pa[v][i]] >= dp[u]) v = pa[v][i]; //rep(i, 0, 20) if (((1 << i) & (dp[v] - dp[u])) == 1) v = pa[v][i]; if (u == v) return u; for (int i = 19; ~i; --i) if (pa[u][i] != pa[v][i]) { u = pa[u][i]; v = pa[v][i]; } return pa[u][0]; } void Compute(int id) { while(!q.empty()) q.pop(); bool vis[N]; mem(vis, 0); d[tp[id]][id] = 0; q.push(P(0, tp[id])); while(!q.empty()) { P p = q.top(); q.pop(); int v = p.second; if (vis[v]) continue; vis[v] = 1; for (int i = head[v]; ~i; i = e[i].next) if (d[e[i].to][id] > d[v][id] + e[i].va) { d[e[i].to][id] = d[v][id] + e[i].va; q.push(P(d[e[i].to][id], e[i].to)); } } } int main() { sc(n), sc(m); Inite(); Rep(i, 1, m) { int u, v, w; sc(u), sc(v), sc(w); addedge(u, v, w); addedge(v, u, w); } DFS(1, 0); Rep(i, 1, 19) Rep(j, 1, n) if (pa[j][i - 1]) pa[j][i] = pa[pa[j][i - 1]][i - 1]; sort(tp + 1, tp + top + 1); top = unique(tp + 1, tp + top + 1) - (tp + 1); Rep(i, 1, top) Compute(i); int cnt; sc(cnt); while(cnt--) { int u, v; sc(u), sc(v); int p = Lca(u, v); LL ans = d[u][0] + d[v][0] - 2 * d[p][0]; Rep(i, 1, top) ans = min(ans, d[u][i] + d[v][i]); pr(ans); } return 0; }