Codeforces 1051F
题意:给出一个边比点数多至多$20$条的无向连通图,每条边有一个边权,多次询问两点间最短路。
由于特殊条件,我们可以随便建一棵生成树(又称 DFS 树),然后就会只有最多$20$条边不在树中。
对于每个二元组询问$(u,v)$,我们答案有两种情况:
1、只走树上的边
2、必须经过一条树外的边
对于第一个很容易用 LCA 求出来。对于第二个,我们记录这条边的一个顶点$i$,然后用 dij 求出顶点的单源最短路,必走这条边的代价等价于$dis(i,u)+dis(i,v)$,因为这样的路径$(u,v)$就能覆盖这条边。两个情况取最小值即可。
知识点:
1、这种有特殊性题目可以将图转化为树,noip 货车运输是同样的思想,区别在于本题是随便一个生成树( DFS 树),而 noip 那题是要最小生成树
2、本题也一定程度上与 相同,最短路之后枚举边求解
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<queue>
#include<climits>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
const LL MAXN = 100000 + 5, LOGS = 17, INF = 300000000000005ll;
struct edge {LL u, v, d;};
struct data {
LL u, d;
bool operator < (const data &b) const {return d > b.d;}
};
std::vector<edge > G[MAXN];
std::set<std::pair<LL, LL> > nt;
std::priority_queue<data > q;
LL n, m, Q, vis[MAXN], dep[MAXN], pre[MAXN][30], h[MAXN], dis[50][MAXN];
void dfs(LL u, LL fa) {
dep[u] = dep[fa] + 1, vis[u] = 1, pre[u][0] = fa;
for (LL i = 1; i <= LOGS; i++) pre[u][i] = pre[pre[u][i - 1]][i - 1];
for (LL i = 0; i < (LL)G[u].size(); i++) {
edge &e = G[u][i];
if (e.v != fa && !vis[e.v]) {
h[e.v] = h[u] + e.d;
dfs(e.v, u);
if (u < e.v) nt.erase(std::make_pair(u, e.v)); else nt.erase(std::make_pair(e.v, u));
}
}
}
void dij(LL st, LL d[MAXN]) {
for (LL i = 0; i <= n; i++) d[i] = INF, vis[i] = 0;
d[st] = 0, q.push((data){st, 0});
while (!q.empty()) {
data p = q.top(); q.pop();
if (vis[p.u]) continue; vis[p.u] = 1;
for (LL i = 0; i < (LL)G[p.u].size(); i++) {
edge &e = G[p.u][i];
if (d[e.v] > d[p.u] + e.d) d[e.v] = d[p.u] + e.d, q.push((data){e.v, d[e.v]});
}
}
}
LL LCA(LL a, LL b) {
if (dep[a] < dep[b]) std::swap(a, b);
for (LL i = LOGS; i >= 0; i--) if (dep[pre[a][i]] >= dep[b]) a = pre[a][i];
if (a == b) return a;
for (LL i = LOGS; i >= 0; i--) if (pre[a][i] != pre[b][i]) a = pre[a][i], b = pre[b][i];
return pre[a][0];
}
LL getdist(LL u, LL v) {return h[u] + h[v] - 2 * h[LCA(u, v)];}
void clean() {
ms(h, 0), ms(vis, 0), ms(dep, 0);
}
int solve() {
clean();
for (LL u, v, d, i = 1; i <= m; i++) {
scanf("%lld%lld%lld", &u, &v, &d);
G[u].push_back((edge){u, v, d}), G[v].push_back((edge){v, u, d});
if (u < v) nt.insert(std::make_pair(u, v)); else nt.insert(std::make_pair(v, u));
}
dfs(1, 0);
//for (LL i = 0; i < (LL)nt.size(); i++) printf("u=%lld v=%lld d=%lld\n", nt[i].u, nt[i].v, nt[i].d);
LL cnt = 0;
for (std::set<std::pair<LL, LL> >::iterator it = nt.begin(); it != nt.end(); it++) {
std::pair<LL, LL> e = *it;
LL &u = e.first, &v = e.second;
//printf("u=%lld v=%lld d=%lld\n", nt[i].u, nt[i].v, nt[i].d);
if (u > v) continue;
dij(u, dis[++cnt]);
}
//for (LL i = 1; i <= n; i++) for (LL j = 1; j <= n; j++) printf("i=%lld j=%lld lca=%lld\n", i, j, LCA(i, j));
scanf("%lld", &Q);
while (Q--) {
LL u, v; scanf("%lld%lld", &u, &v);
LL ans = getdist(u, v);
for (LL i = 1; i <= cnt; i++) {
ans = std::min(ans, dis[i][u] + dis[i][v]);
}
printf("%lld\n", ans);
}
return 0;
}
int main() {
scanf("%lld%lld", &n, &m), solve();
return 0;
}