2021CCPC浙江省赛 D Shortest Path Query
可以发现可以构建出的是一棵树,且树的深度在20层及以内
刚开始想对于每条路径跑最短路,然后都跑一边得出答案,但是发现因为路径可以重叠,所以后面求的路径会修改一些公共路径的最短路,所以不能这么求
考虑什么是不变的,一个点到自己的最短路一定是0,所以从每个点出发,跑它到所有子节点的最短路,这个路一定是这两个点最终的最短路,而且因为这是一个二叉树,所以复杂度为
\(T(n) = 2*T(n/2) + nlogn\)
大约是\(O(n(logn)^2)\)
然后对于每次询问,找所有的公共祖先,求解即可
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
typedef long long ll;
typedef pair<ll, int> P;
vector < P > edge[N];
ll f[N][30];
bool vis[N];
void cmin(ll & a, ll b) { a = min(a, b); }
int main() {
int n, m;
scanf("%d%d", &n, &m);
memset(f, 0x3f, sizeof(f));
for (int i = 1; i <= m; i++) {
int u, v, j; ll w;
scanf("%d%d%lld", &u, &v, &w);
if (u > v) swap(u, v);
edge[u].emplace_back(v, w);
edge[v].emplace_back(u, w);
for (j = 0; (v >> j) != u; j++);
cmin(f[v][j], w);
}
for (int i = 1; i <= n; i++) {
priority_queue < P, vector < P >, greater < P > > Q;
vector < int > c;
Q.emplace(0, i);
int j; f[i][0] = 0;
while (!Q.empty()) {
P u = Q.top(); Q.pop();
if (vis[u.second]) continue;
vis[u.second] = true; c.emplace_back(u.second);
for (j = 0; (u.second >> j) != i; j++);
cmin(f[u.second][j], u.first);
for (auto x:edge[u.second]) {
if (!vis[x.first] && x.first >= i)
Q.emplace(u.first + x.second, x.first);
}
}
for (auto x:c) vis[x] = false;
}
int q; scanf("%d", &q);
while (q--) {
int u, v, j = 0; scanf("%d %d", &u, &v);
ll ans = (1ll << 60);
for (int i = 0; i < 28; i++) {
while ((v >> i) < (u >> j)) j++;
if ((v >> i) == (u >> j))
cmin(ans, f[u][j] + f[v][i]);
}
if (ans > 60ll * 1e9) puts("-1");
else printf("%lld\n", ans);
}
return 0;
}