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
", ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/cminus/p/14751789.html