蓝桥杯/景区导游/c\c++

问题描述

    某景区一共有N个景点,编号1到N。景区之间共有N-1条双向的摆渡车线路相连,形成一棵树状结构。在景点之间往返只能通过这些摆渡车进行,需要花费一定的时间

   小明是这个景区的资深导游,他每天都要按固定顺序带客人游览其中K个景点:A1,A2,... ,Ak。今天由于时间原因,小明决定跳过其中一个景点,只带游客按顺序游览其中K-1个景点。具体来说,如果小明选择跳过Ai,那么他会按顺序带游客游览A1,A2,... ,Ai-1,Ai+1,(1≤i≤K)。请你对任意一个Ai。计算如果跳过这个景点,小明需要花费多少时间在景点之间的摆渡车上?

输入格式

    第一行包含2个整数N和K。

    以下N-1行,每行包含3 个整数u,v,和t ,代表景点u和v之间有摆渡车线路,花费t个单位时间

    最后一行包含K个整数A1,A2,... ,Ak,代表原定游览线路

输出格式

    输出K个整数,其中第i个代表跳过Ai之后,花费在摆渡车上的时间

样例输入

6 4
1 2 1
1 3 1
3 4 2
3 5 2
4 6 3
2 6 5 1

样例输出

10 7 13 14

样例说明

原路线是2-->6-->5-->1

当跳过2时,路线是6-->5-->1,其中6-->5花费时间3+2+2=7,5-->1花费时间2+1=3,总时间花费10.

当跳过6时,路线是2-->5-->1,其中2-->5花费时间1+1+2=4,5-->1花费时间2+1=3,总花费时间7.

当跳过5时,路线,是2-->6-->1,其中2-->6花费时间1+1+2+3=7,6-->1花费时间3+2+1=6,总花费时间13.

当跳过1时,路线是2-->6-->5,其中2-->6花费时间1+2+3=7,6-->5花费时间3+2+2=7,总时间花费14.

//本题主要考察LCA
//若要求u->v的路径长度,等于求u->root+v->root - root->2*LCA(u,v)
//即分别从u和v走到根结点,再减去2倍的根结点到(u,v)的最近公共祖先的路径长度
//同理,若原本的遍历顺序是a->b->c,总路径长度为sum,若要跳过b,则新的路径长度为
//sum-dist(a,b)-dist(b,c)+dist(a,c),可画图验证 
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N=1e5+100;

int n,k;
int deep[N];//深度 
int dp[N][21];//dp[i][j]表示从i结点开始跳2^j步可到达的结点 
vector<int>edge[N];//边 
vector<int>weight[N];//权值 
ll path[N];//原始的游览路线 
ll dist[N];//dist[i]存储i到根结点的距离 

void dfs(int u,int father)//LCA的前置算法(模板) 
{
    deep[u]=deep[father]+1;   //u表示当前dfs节点,father 为其父节点,计算u节点深度
    dp[u][0]=father;          //u向上走2^0=1步即为其父节点
    for(int i=1;i<=20;i++)
    {
        dp[u][i]=dp[dp[u][i-1]][i-1];    //x向上走2^i步等同于x向上走2^i-1后再走2^i-1 步
    }    
    
    for(int i=0;i<edge[u].size();i++)
    {
        int v=edge[u][i],w=weight[u][i];
        if(v==father)continue;
        dist[v]=dist[u]+w;
        dfs(v,u);        //递归处理u子节点
    }
}

int LCA(int x,int y)//求x和y的最近公共祖先(模板) 
{
    if(deep[x]<deep[y])swap(x,y);      //保证x为深度较大的节点
    for(int i=20;i>=0;i--)
    {
        if(deep[dp[x][i]]>=deep[y])
        {
            x=dp[x][i];       //如果x向上走节点深度还是大于y节点,往上跳
        }
    }
    if(x==y)return x;
    for(int i=20;i>=0;i--)
    {
        if(dp[x][i]!=dp[y][i])    //如果跳2^i后不一样,就都跳上去
        {
            x=dp[x][i];
            y=dp[y][i];
        }
    }
    return dp[x][0];
}

ll get_dist(int x,int y)//求x和y的距离 
{
    if(x==0||y==0)return 0;
    return dist[x]+dist[y]-2*dist[LCA(x,y)];     
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<n;i++)//插入n-1条无向边 
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        edge[u].push_back(v);
        edge[v].push_back(u);
        weight[u].push_back(w);
        weight[v].push_back(w);    
    }    
    dfs(1,0);//跑一遍dfs为LCA做准备 
    ll sum=0;//sum存储原始游览路线的总路径长度 
    for(int i=1;i<=k;i++)
    {
        scanf("%d",&path[i]);
        sum+=get_dist(path[i],path[i-1]);//依次累加 
    }
    
    for(int i=1;i<=k;i++)//除去第i个景点 
    {
        ll dist1=get_dist(path[i],path[i-1]);
        ll dist2=get_dist(path[i],path[i+1]);
        ll dist3=get_dist(path[i-1],path[i+1]);
        printf("%lld ",sum-dist1-dist2+dist3);//套公式计算即可 
    }
    printf("\n");
    return 0;
}

  • 29
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值