[CF1051F]The Shortest Statement
题目大意:给出一张$n$个点$m(m-n\leqslant20)$条边的无向图,$q$询问两点之间的最短路。$n,m,q⩽10^5$
题解:询问$10^5$肯定不能每次求最短路,发现$m-n\leqslant20$,也就是说这张图是一棵树加上最多$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\n", ans);
}
return 0;
}

浙公网安备 33010602011771号