把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

P9433 [NAPC-#1] Stage5 - Conveyors 分析

感觉是经典题目。

题目概述

给出一个有 \(n\) 个点并且其中有 \(k\) 个关键点的无根树,要求从 \(s\) 走到 \(t\) 途中必须包含这 \(k\) 个关键点,求最短路径。

分析

考虑以一个关键点作为根。

由于这道题目 \(s,t\) 是会变的,所以我们把注意力放在这几个关键点上面。

我们根据这些关键点的位置把他划分成一个尽量小的区域,考虑这一部分对答案的贡献是不是一个定值。

我们可以考虑特殊情况:即 \(s,t\) 都在这个区域里面。

那么显然地,答案就是这个区域的所有边权和的两倍减去 \(s\rightarrow t\) 的最短路。

然后如果其中一个或者两个在外面的情况直接找到能到这个区域的最近的一个点即可,加上就行了。

代码

时间复杂度 \(\mathcal{O}((n+q)\log n)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#define int long long
#define N 100005
#define M 25
using namespace std;
int n,q,k,fa[N][M],dep[N],num[N],dis[N];
bool vis[N];
struct edge{
    int v,w;
};
vector<edge> g[N];
void dfs0(int cur,int father) {
    dep[cur] = dep[father] + 1;
    fa[cur][0] = father;
    for (auto i : g[cur])
        if (i.v != father) dis[i.v] = dis[cur] + i.w,dfs0(i.v,cur),num[cur] += num[i.v];
}
int LCA(int x,int y) {
    if (x == y) return x;
    if (dep[x] < dep[y]) x ^= y ^= x ^= y;
    for (int j = 20;j >= 0;j --)
        if (dep[fa[x][j]] >= dep[y]) x = fa[x][j];
    if (x == y) return x;
    for (int j = 20;j >= 0;j --)
        if (fa[x][j] != fa[y][j]) x = fa[x][j],y = fa[y][j];
    return fa[x][0];
}
int getdis(int x,int y) {
    return dis[x] + dis[y] - 2 * dis[LCA(x,y)];
}
int weight;
void dfs(int cur,int father) {
    if (!num[cur]) return;
    vis[cur] = 1;
    for (auto i : g[cur])
        if (i.v != father) {
            dfs(i.v,cur);
            if (vis[i.v]) weight += i.w;
        }
}
int getmn(int cur) {
    if (vis[cur]) return cur;
    for (int j = 20;j >= 0;j --)
        if (!vis[fa[cur][j]] && fa[cur][j]) cur = fa[cur][j];
    return fa[cur][0];
}
signed main(){
    cin >> n >> q >> k;
    for (int i = 1;i < n;i ++) {
        int u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        g[u].push_back({v,w});
        g[v].push_back({u,w});
    }
    int x;
    for (int i = 1;i <= k;i ++) {
        scanf("%lld",&x);
        vis[x] = 1,num[x] = 1;
    }
    dfs0(x,0);
    for (int j = 1;j <= 20;j ++)
        for (int i = 1;i <= n;i ++)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
    dfs(x,0);
    // cout << weight << '\n';
    for (int s,t;q --;) {
        scanf("%lld%lld",&s,&t);
        int mns = getmn(s),mnt = getmn(t);
        // cout << mns << ' ' << mnt << '\n';
        printf("%lld\n",2 * weight - getdis(mns,mnt) + getdis(mns,s) + getdis(mnt,t));
    }
    return 0;
}
posted @ 2025-11-21 15:32  high_skyy  阅读(3)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end