题目大意:给出一张$n$个点$m(m-nleqslant20)$条边的无向图,$q$询问两点之间的最短路。$n,m,q⩽10^5$
题解:询问$10^5$肯定不能每次求最短路,发现$m-nleqslant20$,也就是说这张图是一棵树加上最多$21$一条非树边,这$21$条非树边最多连接$42$个不同的点。答案要么是在树上的答案($dep_u+dep_v-2dep_{LCA(u,v)}$),要么就经过了至少一条非树边。
至少经过一条非树边比较难处理。可以对每个连接了非树边的点跑一遍最短路,因为这是唯一导致答案变小的方案。
卡点:求$LCA$时$x,y$写反,导致减乘负数($Codeforces$评测机好评,每次$RE$都可以帮我找到哪出锅了)
C++ Code:
#include <cstdio> #include <cstring> #include <queue> #define maxn 100010 #define maxm 100030 #define maxk 45 const long long inf = 0x3f3f3f3f3f3f3f3f; inline long long min(long long a, long long b) {return a < b ? a : b;} int head[maxn], cnt = 1; struct Edge { int to, nxt; long long w; bool tree; } e[maxm << 1]; inline void addE(int a, int b, long long c) { e[++cnt] = (Edge) {b, head[a], c, false}; head[a] = cnt; } int n, m; namespace Tree { #define M 18 int fa[maxn][M + 1], dep[maxn]; long long sum[maxn]; void dfs(int u) { for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (dep[v]) continue; fa[v][0] = u; dep[v] = dep[u] + 1; sum[v] = sum[u] + e[i].w; e[i].tree = e[i ^ 1].tree = true; dfs(v); } } inline void init(int rt) { dep[rt] = 1; dfs(rt); for (int i = 1; i <= M; i++) for (int j = 1; j <= n; j++) fa[j][i] = fa[fa[j][i - 1]][i - 1]; } inline int LCA(int x, int y) { if (x == y) return x; if (dep[x] < dep[y]) std::swap(x, y); for (int i = dep[x] - dep[y]; i; i &= i - 1) x = fa[x][__builtin_ctz(i)]; if (x == y) return x; for (int i = M; ~i; i--) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0]; } #undef M } using Tree::LCA; using Tree::sum; int S[maxk], top; bool vis[maxn]; long long d[maxk][maxn]; struct node { int p; long long d; node () {p = d = 0;} node (int __p, long long __d = 0) {p = __p, d = __d;} inline bool operator < (const node &rhs) const {return d > rhs.d;} }; std::priority_queue<node> q; void dij(int S, long long *d) { while (!q.empty()) q.pop(); for (int i = 1; i <= n; i++) d[i] = inf, vis[i] = false; d[S] = 0; q.push(node(S)); while (!q.empty()) { int u = q.top().p; q.pop(); if (vis[u]) continue; vis[u] = true; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (d[v] > d[u] + e[i].w) { d[v] = d[u] + e[i].w; q.push(node(v, d[v])); } } } } int main() { scanf("%d%d", &n, &m); for (int i = 0, a, b, c; i < m; i++) { scanf("%d%d%d", &a, &b, &c); addE(a, b, c); addE(b, a, c); } Tree::init(1); for (int i = 2; i <= cnt; i += 2) { if (!e[i].tree) { int u = e[i ^ 1].to, v = e[i].to; if (!vis[u]) S[++top] = u, vis[u] = true; if (!vis[v]) S[++top] = v, vis[v] = true; } } for (int i = 1; i <= top; i++) dij(S[i], d[i]); int QQ; scanf("%d", &QQ); while (QQ --> 0) { int u, v; scanf("%d%d", &u, &v); long long ans = sum[u] + sum[v] - sum[LCA(u, v)] * 2; for (int i = 1; i <= top; i++) { ans = min(ans, d[i][u] + d[i][v]); } printf("%lld ", ans); } return 0; }