CF1051F The Shortest Statement 题解

题目链接

给定一个树,在树上加k条边,求全源最短路。(\(n \le 10^5 \space, k \le 20\))

显然利用树的性质,考虑如何处理加的20条边即可。可以枚举所有经过的非树边,对每个非树边的端点依次更新答案,再结合树上路径长度即可。

最小生成树+lca+dij即可解决的一道紫题...

写的时候要注意不同的图、树的混淆问题。

把lca板子写错调了一小时??? 有时候对树上深度总有种从下到上的错觉。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#include <assert.h>
#include <map>
#define ll long long
using namespace std;
const ll N = 100005, M = 200005,INF = 1e14;
ll n, m, Q;
ll cnt1, head1[M], cnt2, head2[M];
ll cnt, vis[N], dep[N], fa[N], f[N][23];
ll dis[45][N], treedis[N], tot,await[N];
struct edge {
	ll from, to, next;
	ll dis;
} e1[M], e2[M];
struct EDGE {
	ll u, v, d;
} e[M];
int read() {
	register int x=0,f=1;
	register char s=getchar();
	while(s>'9'||s<'0') {
		if(s=='-') f=-1;
		s=getchar();
	}
	while(s>='0'&&s<='9') {
		x=x*10+s-'0';
		s=getchar();
	}
	return x*f;
}
void add1(ll u, ll v, ll dis) {
	e1[++cnt1].from = u;
	e1[cnt1].to = v;
	e1[cnt1].next = head1[u];
	e1[cnt1].dis = dis;
	head1[u] = cnt1;
}
void add2(ll u, ll v, ll dis) {
	e2[++cnt2].to = v;
	e2[cnt2].from = u;
	e2[cnt2].next = head2[u];
	e2[cnt2].dis = dis;
	head2[u] = cnt2;
}
int cmp(EDGE a, EDGE b) {
	return a.d < b.d;
}
ll find(ll k) {
	return fa[k] == k ? k : fa[k] = find(fa[k]);
}
void dij(ll k) {
	for (int i=1;i<=n;i++)
	  vis[i]=0,dis[k][i]=INF;
	priority_queue<pair<ll,int> > q;
	dis[k][await[k]] = 0ll;
	q.push(make_pair(0,await[k]));
	while (!q.empty()) {
		ll x = q.top().second;
		q.pop();
		if (vis[x]) continue;
		vis[x] = 1;
		for (ll i = head1[x]; i; i = e1[i].next) {
			ll v = e1[i].to;
			if (dis[k][v] > dis[k][x] + e1[i].dis) {
				dis[k][v] = dis[k][x] + e1[i].dis;
				q.push(make_pair(-dis[k][v],v));
			}
		}
	}
}
void dfs(ll k) {
	for (int i=1; i<=20; i++) f[k][i]=f[f[k][i-1]][i-1];
	for (ll i = head2[k]; i; i = e2[i].next) {
		ll v = e2[i].to;
		if (v == f[k][0]) continue;
		f[v][0]=k;
		treedis[v]=treedis[k]+e2[i].dis;
		dep[v]=dep[k]+1;
		dfs(v);
	}
}
ll lca(ll a, ll b) {
	if (dep[a] < dep[b]) swap(a, b);
	for (ll i = 20; i >= 0; i--)
		if (dep[f[a][i]] >= dep[b])
			a = f[a][i];
	if (a == b) return a;
	for (ll i = 20; i >= 0; i--)
		if (f[a][i] != f[b][i]) a = f[a][i], b = f[b][i];
	return f[a][0];
}
int main() {
	n=read(),m=read();
	for (ll i = 1; i <= n; i++) fa[i] = i;
	for (ll i = 1; i <= m; i++) {
		e[i].u=read(),e[i].v=read(),e[i].d=read();
		add1(e[i].u, e[i].v, e[i].d),add1(e[i].v, e[i].u, e[i].d);
	}
	sort(e + 1, e + 1 + m, cmp);
	for (ll i = 1; i <= m; i++) {
		ll f1 = find(e[i].u), f2 = find(e[i].v);
		if (f1 != f2) {
			add2(e[i].u, e[i].v, e[i].d);
			add2(e[i].v, e[i].u, e[i].d);
			fa[f1] = f2;
		} else {
			await[++cnt]=e[i].u;
			await[++cnt]=e[i].v;
		}
	}
	for (int i=1; i<=cnt; i++)  dij(i);
	cin >> Q;
	dfs(1);
	while (Q--) {
		ll x = read(), y = read();
		ll ans = treedis[x] + treedis[y] - 2 * treedis[lca(x, y)];
		for (ll i = 1; i <= cnt; i++)
			ans=min(ans,dis[i][x] + dis[i][y]);
		cout << ans << endl;
	}
}

posted @ 2021-11-11 17:46  Kinuhata  阅读(6)  评论(0编辑  收藏  举报