P4239追捕游戏

问题描述

何老板最近在玩一款追铺游戏,游戏虽然简单,何老板仍旧乐此不疲。

 游戏地图中有n座城市由n-1条双向道路连接。任意两座城市都可相互到达。一名罪犯从A城市出发沿最短路线逃往B城市。在罪犯出发的同时,何老板控制一名警察从C城市出发去追捕那名罪犯。每条道路都有一定的长度(单位米)。罪犯和警察行走的速度相同,都是1秒钟行走1米。

 若罪犯到达B城市时还没有被抓住,何老板就输掉了这局游戏。何老板总共玩了m局游戏,每局游戏开始前,何老板想知道他是否能赢下这局游戏,如果能,警察最少行走多少米才能抓到罪犯?

输入格式

第一行,两个整数n和m

 接下来n-1行,每行三个整数X,Y,Z,表示城市X和Y之间有一条长度为Z的道路相连。

 接下来m行,每行三个整数A,B,C。

输出格式

m行,每行对应一局游戏的结果。若能抓捕到罪犯。输出一个整数,表示警察最少需要行走的距离。若无法抓到罪犯,输出-1。

样例输入

11 2
1 2 6
1 3 3
1 4 3
3 5 2
3 6 5
4 7 9
6 10 3
5 8 4
5 9 3
8 11 8
11 9 10
2 4 7

样例输出

10
9

提示

【样例解释】

第1局在5号城市抓住罪犯。
第2局在4号城市抓住罪犯。

【数据范围】
对于约40% 的数据:1<=N,M<=2000

对于约100% 的数据:1<=N,M<=100000 1<=道路的长度<=10000

题解

树上两点有且仅有一条路径到达,所以两者到同一节点必定会在一点汇合,此点最坏情况为到此节点。

注意lca两点向上跳时最小可为0

代码

#include<stdio.h> 
#include<algorithm> 
#include<cstdio> 
#include<cmath> 
#include<cstring> 
#include<queue> 
#include<iostream> 
using namespace std; 
#define inf 1e9 
#define pr cout<< 
#define maxn 200005 
int cnt; 
int Last[maxn],Next[maxn],Len[maxn],End[maxn]; 
int f[maxn][20],dis[maxn][20]; 
int n,m; 
int ans; 
int dep[maxn]; 
void insert(int a,int b,int c) 
{ 
    Next[++cnt]=Last[a]; 
    Last[a]=cnt; 
    End[cnt]=b; 
    Len[cnt]=c; 
} 
void dfs(int x){ 
    int i,j; 
    dep[x]=dep[f[x][0]]+1; 
    int k=ceil(log2(dep[x])); 
    for(i=1;i<=k;i++){

    f[x][i]=f[f[x][i-1]][i-1]; 
       dis[x][i]=dis[x][i-1]+dis[f[x][i-1]][i-1]; 


    } 

    for(i=Last[x];i;i=Next[i]){ 
        int en=End[i]; 
        if(en!=f[x][0]){ 
            f[en][0]=x; 
            dis[en][0]=Len[i]; 
            dfs(en); 
        } 
    } 
} 
int lca(int a,int b) 
{ 
    int i,s,j,k; 
    ans=0; 
    k=ceil(log2(n));  
    if(dep[a]<dep[b]) swap(a,b); 
    s=dep[a]-dep[b]; 
    for(i=0;i<=k;i++){ 
        if(s&(1<<i)){ 
            ans+=dis[a][i]; 
            a=f[a][i]; 
        } 
    } 
    if(b==a) return a; 
    s=ceil(log2(n));
    for(i=s;i>=0;i--){ 
        if(f[a][i]!=f[b][i]){ 
            ans+=dis[a][i];ans+=dis[b][i]; 
            a=f[a][i];b=f[b][i]; 
        } 
    } 
    ans+=dis[a][0];ans+=dis[b][0]; 
    return f[a][0]; 
} 
int main() 
{ 
    int i,j; 
    scanf("%d%d",&n,&m); 
    int x,y,z; 
    for(i=1;i<=n-1;i++){ 
        scanf("%d%d%d",&x,&y,&z); 
        insert(x,y,z); 
        insert(y,x,z); 
    } 
    dfs(1); 


    int l1,l2,l3; 
    int a1,a2,a3; 
    int s1,s2,t;
    for(i=1;i<=m;i++){ 
      scanf("%d%d%d",&x,&y,&z); 

      l1=lca(x,y);
      l2=lca(y,z);
      l3=lca(x,z);
      if(l1==l2){
       s1=lca(l3,x);s1=ans;
       s2=lca(l3,z);s2=ans;
    }
    else if(l1==l3){
        s1=lca(l2,x);s1=ans;
       s2=lca(l2,z);s2=ans;
    }
    else if(l2==l3){
          s1=lca(l1,x);s1=ans;
       s2=lca(l1,z);s2=ans;
    }

    if(s1<s2) cout<<"-1"<<endl;
    else cout<<s2<<endl;
   }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值