nkoj 4239 追捕游戏

问题描述

何老板最近在玩一款追铺游戏,游戏虽然简单,何老板仍旧乐此不疲。
游戏地图中有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

乱画的图

在小偷从a到b的过程中,必然在路径上有且仅有一点T,使T与b相连,否则构成环。
如果找到这个T点,若警察在小偷之前或者恰好到达T,则可以抓到。
那么,如何找到T点?
如图,我们显然可以得到这个结论:

a,b,c三点两两LCA,则T点为其中不同的那个最近公共祖先。

另外,还需要注意边带权即可。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstdlib>
using namespace std;
const int need=100060;
const double lg2=log(2);
int n,m,i,j;
int las[need],nex[need],en[need],len[need];
int dep[need],fa[need][22],dis[need];
int tot=0,d=0;
inline void re(int &d)
{
    bool f=false;char t=getchar();
    while(t<'0'||t>'9'){if(t=='-')f=true;t=getchar();}
    for(d=0;t>='0'&&t<='9';t=getchar())d=(d<<1)+(d<<3)+t-'0';
    d=(f?(-d):(d));
}
void add(int a,int b,int c)
{
    tot++;
    nex[tot]=las[a];
    en[tot]=b;
    len[tot]=c;
    las[a]=tot;
}
bool vis[need];
void dfs(int s)
{
    int y;
    int k=ceil(log(dep[s])/lg2);
    d=max(d,k);
    for(int i=1;i<=k;i++) fa[s][i]=fa[fa[s][i-1]][i-1];
    for(int t=las[s];t;t=nex[t])
    {
        y=en[t];
        if(vis[y]) continue;
        vis[y]=true;
        dep[y]=dep[s]+1;
        dis[y]=dis[s]+len[t];
        fa[y][0]=s;
        dfs(y);
    }
}
int lca(int v,int u)
{
    if(dep[u]>dep[v]) swap(u,v);
    int p=dep[v]-dep[u];
    for(int i=0;i<=d;i++) if((p>>i)&1) v=fa[v][i];
    if(u==v) return v;
    for(int i=d;i>=0;i--) if(fa[u][i]!=fa[v][i]) v=fa[v][i],u=fa[u][i];
    return fa[u][0];
}
int getdis(int a,int b)
{
    int c=lca(a,b);
    return dis[a]-dis[c]+dis[b]-dis[c];
}
int main()
{
    //freopen("catch.in","r",stdin);
    //freopen("catch.out","w",stdout);
    int a,b,c,e,f[3],flag=0;
    re(n);re(m);
    for(i=1;i<n;i++)
    {
        re(a);re(b);re(c);
        add(a,b,c),add(b,a,c);
    }
    dfs(1);
    for(i=1;i<=m;i++)
    {
        re(a);re(b);re(c);
        f[0]=lca(a,b);
        f[1]=lca(b,c);
        f[2]=lca(a,c);
        if(f[0]==f[1])e=f[2];
        else if(f[0]==f[2])e=f[1];
        else if(f[1]==f[2])e=f[0];
        int x=getdis(e,a),y=getdis(e,c);
        if(x>=y)printf("%d\n",y);
        else printf("-1\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值