景区导游
题目
link
思路
这一题有\(n\)个点,还有\(n-1\)条无向边,每条边有一个权值,这个信息告诉我们,这其实就是树结构
并且给了\(K\)个点,让我们按照顺序带游客游览
但是现在让我们选择\(K-1\)个点,并且顺序游览
现在要求对\(K\)个点跳过任意一个,计算跳过这个点,游览完需要花费多少时间
(1) 我们需要注意的是,如何计算两个点\((u, v)\)距离:
- 首先确定一个根节点\(root\)
- 计算\(root -> u\)和\(root -> v\)的距离,但是重复计算了一段\(2 * root -> lca(u, v)\)
- 因此\(dist(u, v) = dist[u] + dist[v] - dist[lca(u, v)]\)
(2) 另一个难点在于,我们要先把整个\(K\)个点的完整花费时间算出来,然后我们再挨个去除某个点的值
例如\(a -> b -> c\),现在要把b去除,则\(sum - dist[a, b] - dist[b, c] + dist[a, c]\)
#include <bits/stdc++.h>
#define pb push_back
#define int long long
#define SZ(v) (int)(v.size())
using namespace std;
const int N = 1e5+10;
int _;int n, k;
int u, v, t;
int fa[N][30], dep[N];
vector<int> e[N], w[N];
int path[N], dist[N];void dfs(int u, int father) {dep[u] = dep[father] + 1;fa[u][0] = father;for(int i = 1; i <= 20; i++) {fa[u][i] = fa[fa[u][i-1]][i-1];}for(int i = 0; i < SZ(e[u]); i++) {int v = e[u][i], ww = w[u][i];if(v == father) continue;dist[v] = dist[u] + ww;dfs(v, u);}
}int LCA(int u, int v) {if(dep[u] < dep[v]) {swap(u, v);}for(int i = 20; i >= 0; i--) {if(dep[fa[u][i]] >= dep[v]) {u = fa[u][i];}}if(u == v) return u;for(int i = 20; i >= 0; i--) {if(fa[u][i] != fa[v][i]) {u = fa[u][i];v = fa[v][i];}}return fa[u][0];
} int get_dist(int u, int v) {if(u == 0 || v == 0) return 0;return dist[u] + dist[v] - 2 * dist[LCA(u, v)];
}void solve() {cin >> n >> k;for(int i = 1; i < n; i++) {cin >> u >> v >> t;e[u].pb(v); e[v].pb(u);w[u].pb(t); w[v].pb(t);}dfs(1, 0);int sum = 0;for(int i = 1; i <= k; i++) {cin >> path[i];sum += get_dist(path[i], path[i-1]);}// a -> b -> c// b去掉: sum - [a, b] - [b, c] + [a, c];for(int i = 1; i <= k; i++) { int dis1 = get_dist(path[i], path[i-1]);int dis2 = get_dist(path[i], path[i+1]);int dis3 = get_dist(path[i-1], path[i+1]);int ans = sum - dis1 - dis2 + dis3;cout << ans << " ";}
}signed main() {ios::sync_with_stdio(false);cin.tie(nullptr);_ = 1;// cin >> _;while(_--) {solve();} return 0;
}