lanqiao3516 景区导游(LCA)

This commit is contained in:
2025-03-17 20:19:31 +08:00
parent b28d6fef87
commit 0e38be69ec

91
14lanqiao/test9-2.cpp Normal file
View File

@@ -0,0 +1,91 @@
/* lanqiao3516 景区导游(LCA)
LCA-最近公共祖先
比如原本的遍历顺序是 a->b->c, 总路径长度为sum若要跳过b则新的路径长度为
sum-d(a,b)-d(b,c)+d(a,c)
其中 d(a,b、d(b,c、d(a,c使用LCA求解更快
若要求u->v的路径长度, 等于求u-> root + v -> root - 2*(root->LCA(u ,v))
即分别从u和v走到根结点再减去2倍的根结点到 (u,v) 的最近公共祖先的路径长度
倍增法求LCA时间复杂度-O(nlogn单次LCA查询时间复杂度-O(1ognk次查询时间复杂度-O(klogn
总时间复杂度O(n+k)1ogn由于n,k同級该算法时间复杂度为O(nlogn)
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;
int n, k;
// 邻接表存图
vector<pair<int, int>> g[N];
int deep[N]; // 深度数组
int fa[N][21]; // 倍增数组, fa[i][j]表示从i节点开始跳2^j步可到达的节点
int p[N]; // 原始的游览路线
int d[N]; // d[i]存储i到根节点的距离
// 预处理深度数组
void dfs(int u, int father){
deep[u] = deep[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 < g[u].size(); i++){
int v = g[u][i].first, w = g[u][i].second;
if(v == father) continue;
d[v] = d[u] + w;
dfs(v, u);
}
}
// 倍增求LCA模版
int LCA(int x, int y){
if(deep[x] < deep[y]) swap(x, y);
for(int i = 20; i >= 0; i--){
if(deep[fa[x][i]] >= deep[y]){
x = fa[x][i];
}
}
if(x == y) return x;
for(int i = 20; i >= 0; i--){
if(fa[x][i] != fa[y][i]){
x = fa[x][i], y = fa[y][i];
}
}
return fa[x][0];
}
// 计算x和y的距离
int calc(int x, int y){
if(x == 0 || y == 0) return 0;
return d[x] + d[y] - 2*d[LCA(x, y)];
}
signed main(){
cin >> n >> k;
for(int i = 1; i < n; i++){
int u, v, w; cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
dfs(1, 0);
int sum = 0;
for(int i = 1; i <= k; i++){
cin >> p[i];
sum += calc(p[i], p[i-1]);
}
for(int i = 1; i <= k; i++){ // 循环枚举去除第i个节点
int d1 = calc(p[i], p[i-1]);
int d2 = calc(p[i], p[i+1]);
int d3 = calc(p[i-1], p[i+1]);
cout << sum - d1 - d2 + d3 << " ";
}
return 0;
}
/* test sanples -> 10 7 13 14
6 4
1 2 1
1 3 1
3 4 2
3 5 2
4 6 3
2 6 5 1
*/