牛客小白月赛13 小A的最短路(lca+RMQ)

链接:https://ac.nowcoder.com/acm/contest/549/F
来源:牛客网

题目描述

小A这次来到一个景区去旅游,景区里面有N个景点,景点之间有N-1条路径。小A从当前的一个景点移动到下一个景点需要消耗一点的体力值。但是景区里面有两个景点比较特殊,它们之间是可以直接坐观光缆车通过,不需要消耗体力值。而小A不想走太多的路,所以他希望你能够告诉它,从当前的位置出发到他想要去的那个地方,他最少要消耗的体力值是多少。

输入描述:

第一行一个整数N代表景区的个数。
接下来N-1行每行两个整数u,v代表从位置u到v之间有一条路径可以互相到达。
接下来的一行两个整数U,V表示这两个城市之间可以直接坐缆车到达。
接下来一行一个整数Q,表示有Q次询问。
接下来的Q行每行两个整数x,y,代表小A的位置在x,而他想要去的地方是y。

输出描述:

x,yxy对于每个询问下x,y输出一个结果,代表x到y消耗的最少体力
示例1

输入


4
1 2
1 3
2 4
3 4
2
1 3
3 4

输出


1
0

解题思路:很明显是lca的板子题,唯一不同的就是加了一条权值为零的边而已,我们假设边x-y权值为零,需要求u-v的最小距离,dis【i,j】为i到j的最小距离,此时u-v的最小距离为min(dis【u,v】,min(dis【u,x】+dis【y,v】,dis【u,y】+dis【x,v】))了。
在这,我就简单讲一下lca的RMQ的求法吧。
两个结点的最近公共祖先的求法:

 

就如上面的一棵树,我们按DFS遍历一遍,把遍历到的结点加入到某个数组中可以得到一个序列:1 2 4 2 5 7 9 7 5 8 5 2 1 3 6 3 1
他们的深度依次为:                                  0 1 2 1 2 3 4 3 2 3 2 1 0 1 2 1 0
要查找某两个结点的最近公共祖先,即查找这这个序列中两个结点所对应的这一段的深度最小的那个结点。比如查找5和6的最近公共祖先,他们对应的结点序列为:
5 7 9 7 5 8 5 2 1 3 6,深度为
2 3 4 3 2 3 2 1 0 1 2,深度最小为1结点(深度为0)。这里可以使用RMQ来维护(当然也可以用线段树),不熟悉RMQ的可以看:传送门。(感觉讲的很渣啊。。。)
当然,求lca的还有别的方法,等以后有机会再更新吧(目前不会QAQ)。
这里贴上本题的AC代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<stack>
#include<cstdio>
#include<map>
#include<set>
#include<string>
#include<queue>
using namespace std;
#define inf 0x3f3f3f3f
#define ri register int
typedef long long ll;

inline ll gcd(ll i,ll j){
    return j==0?i:gcd(j,i%j);
}
inline ll lcm(ll i,ll j){
    return i/gcd(i,j)*j;
}
inline void read(int &x){
    char ch=x=0;
    int f=1;
    while(!isdigit(ch)){
        ch=getchar();
        if(ch=='-'){
            f=-1;
        }    
    }
    while(isdigit(ch))
        x=x*10+ch-'0',ch=getchar();
        x=x*f;
}
const int maxn=3e5+5;
struct st1{
    int to,w;
    st1():to(0),w(0){
    }
    st1(int to,int w):to(to),w(w){
    }
};
vector<st1> vec[maxn];
int dep[maxn];
int in[maxn*2];//某个点在欧拉序第一次出现的位置
int co;
int vis[maxn];
int st[maxn*2][30];//存运行dfs时候的序列(好像叫欧拉序)
void dfs(int u,int cnt){
    vis[u]=1;
    st[++co][0]=u;
    in[u]=co; 
    dep[u]=cnt;
    for(int i=0;i<vec[u].size();i++){
        st1 tem=vec[u][i];
        if(vis[tem.to]==0){
            dfs(tem.to,cnt+1);
            st[++co][0]=u;
        }    
    }
}
void getst(){
    for(int i=1;(1<<i)<=co;i++){
        for(int be=1;be+(1<<i)-1<=co;be++){
            if(dep[st[be][i-1]]<dep[st[be+(1<<(i-1))][i-1]]){
                st[be][i]=st[be][i-1];
            }
            else{
                st[be][i]=st[be+(1<<(i-1))][i-1];
            }
        }
    }
}
int rmq(int l,int r){
    int x=in[l];
    int y=in[r];
    if(x>y)swap(x,y);
    int tem=floor(log(y-x+1)/log(2));
    int tem1=st[x][tem];
    int tem2=st[y-(1<<tem)+1][tem];
    if(dep[tem1]<dep[tem2])return tem1;
    else return tem2;
}
int main(){
    int n;
    int u,v;
    co=0;
    read(n);
    for(int i=1;i<n;i++){
        read(u);read(v);
        vec[u].push_back(st1(v,1));
        vec[v].push_back(st1(u,1));
    }
    read(u);read(v);
    int k;
    dfs(1,0);
    getst();
    scanf("%d",&k);
    int x,y;
    for(int i=0;i<k;i++){
        read(x);read(y);
        int ans;
        ans=dep[x]+dep[y]-2*dep[rmq(x,y)];
        ans=min(ans,dep[x]+dep[u]-2*dep[rmq(x,u)]+dep[y]+dep[v]-2*dep[rmq(y,v)]);
        ans=min(ans,dep[x]+dep[u]-2*dep[rmq(y,u)]+dep[y]+dep[v]-2*dep[rmq(x,v)]);
        printf("%d\n",ans);    
    }
    return 0;
}

 

posted @ 2019-04-15 11:19  风雨兼程-zhi  阅读(212)  评论(0编辑  收藏  举报