题目链接:https://www.lanqiao.cn/problems/3516/learning/
个人评价:难度 4 星(满星:5)
 前置知识:LCA
整体思路
- 假设能快速算出任意两个节点 u , v u,v u,v 之间的距离,就可以先算一遍所有 A i A_i Ai 与 A i + 1 A_{i+1} Ai+1 之间的距离总和,记为 d i s S u m = ∑ i = 1 i = K − 1 d i s A i , A i + 1 disSum=\sum_{i=1}^{i=K-1} dis_{A_i,A_{i+1}} disSum=∑i=1i=K−1disAi,Ai+1,然后考虑跳过每一个 A i ( 2 ≤ i ≤ K − 1 ) A_i~(2 \leq i \leq K-1) Ai (2≤i≤K−1) 节点的情况:把 d i s S u m disSum disSum 减去 ( d i s A i − 1 , A i + d i s A i , A i + 1 ) (dis_{A_{i-1},A_i} + dis_{A_i,A_{i+1}}) (disAi−1,Ai+disAi,Ai+1),再加上 d i s A i − 1 , A i + 1 dis_{A_{i-1},A_{i+1}} disAi−1,Ai+1 就是跳过节点 A i A_i Ai 的结果(当 i = 1 i=1 i=1 与 i = K i=K i=K 时直接把 d i s S u m disSum disSum 分别减去 d i s A 1 , A 2 dis_{A_1,A_2} disA1,A2 和 d i s A K − 1 , A K dis_{A_{K-1},A_K} disAK−1,AK 即可);
- 先用一个 dfs跑出所有节点 u u u 到根节点的距离 d i s u , r o o t dis_{u,root} disu,root,然后任意两个节点之间的距离,就是它们到根节点之间的距离总和减去两倍的它们最近公共祖先到根节点的距离,假设用LCA求出最近公共祖先节点为 f f f,则任意两点间距离为
d i s u , v = d i s u , r o o t + d i s v , r o o t − 2 × d i s f , r o o t dis_{u,v}=dis_{u,root}+dis_{v,root}-2\times dis_{f,root} disu,v=disu,root+disv,root−2×disf,root
过题代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int Log = 20;
const int maxn = 100000 + 100;
struct Node {
    int pos;
    LL dis;
    Node() {}
    Node(int pos, LL dis) : pos(pos), dis(dis) {}
};
int n, k, u, v, t, idCnt;
int st[maxn][Log];
LL disSum;
vector<Node> G[maxn];
int id[maxn], a[maxn];
LL dis[maxn], disPre[maxn];
void dfs(int root, int fa) {
    id[root] = ++idCnt;
    st[root][0] = fa;
    for(int j = 1; j < Log; ++j) {
        st[root][j] = st[st[root][j - 1]][j - 1];
    }
    for (Node &node: G[root]) {
        int pos = node.pos;
        if (pos != fa) {
            dis[pos] = dis[root] + node.dis;
            dfs(pos, root);
        }
    }
}
int lca(int x, int y) {
    if(x == y) {
        return x;
    }
    if(id[x] > id[y]) {
        swap(x, y);
    }
    for(int i = Log - 1; i >= 0; --i) {
        if(id[st[y][i]] > id[x]) {
            y = st[y][i];
        }
    }
    return st[y][0];
}
int main() {
#ifdef ExRoc
    freopen("test.txt", "r", stdin);
#endif // ExRoc
    ios::sync_with_stdio(false);
    cin >> n >> k;
    for (int i = 1; i < n; ++i) {
        cin >> u >> v >> t;
        G[u].push_back(Node(v, t));
        G[v].push_back(Node(u, t));
    }
    dfs(1, 1);
    for (int i = 1; i <= k; ++i) {
        cin >> a[i];
    }
    for (int i = 2; i <= k; ++i) {
        int f = lca(a[i], a[i - 1]);
        disPre[i] = dis[a[i]] + dis[a[i - 1]] - 2 * dis[f];
        disSum += disPre[i];
    }
    cout << disSum - disPre[2] << " ";
    for (int i = 1; i + 2 <= k; ++i) {
        int f = lca(a[i], a[i + 2]);
        cout << disSum - disPre[i + 1] - disPre[i + 2] + dis[a[i]] + dis[a[i + 2]] - 2 * dis[f] << " ";
    }
    cout << disSum - disPre[k] << endl;
    return 0;
}
 
                   
                   
                   
                   
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                   1131
					1131
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            