【Lca 在线st算法】hdu 2586 How far away ?

LCA 在线st算法
题意: 对于一个图,n(<=40000)个点,给出n-1条边(u,v,w,), m(<=200)个询问给出两点,问其最短距离
题解: LCA问题(求最近公共祖先问题),在线算法(对于每个询问得出答案,相对离线算法需要得到所有询问后一次性给出答案)。
以任意一点作为根节点,两点最短距离为,两点分别到根节点的距离和减去两倍的最近公共祖先到根节点的距离。
st算法: 基于RMQ(dp)的算法;
dfs预先处理出ver[] (向下递归和回溯经过的点),dep[] (同上经过点的深度),first[] (对于ver,点第一次出现在ver[]数组的位置)。
分别介绍它们的作用:
first[] 可以对于给出的点得到在dep[]上的范围,[思考dp的过程]这个范围肯定包含了其同棵树的根节点(即最近公共祖先)
dep[] 在得到的范围里用RMQ寻找里面最小值的位置(即公共祖先的位置)
ver[] 对于得到的位置只是dep[]数组上的位置,ver[]是相对位置上对应的点
注意: 1.每个节点可能遍历两边,dep,ver数组开N*2
2. first[]点在dep[]对应的id,(l,r)谁大谁小还不确定的

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
//#pragma comment(linker, "/STACK:102400000,102400000")

const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF=0x3f3f3f3f;
const LL mod = 1e9+7;
const int N = 100000+10;
const int M = 2500000;

struct Node
{
    int to;
    int val;
};
vector<Node> ma[N];     //vector 记录图
int dir[N],ver[N],first[N],dep[N];
bool vis[N];
int top;
void dfs(int n,int depth,int dist)  //dfs处理需要数组
{
    vis[n]=true;
    ver[++top]=n; first[n]=top; dep[top]=depth; dir[n]=dist;
    for(int i = 0; i < ma[n].size(); i++){
        Node temp = ma[n][i];
        if(!vis[temp.to]){
            dfs(temp.to,depth+1,dist+temp.val);
            ver[++top]=n; dep[top]=depth;   //这俩数组回溯时也需要记录
        }
    }
}
int dp[N][30];          //代表区间(i,i+(1<<j)-1)的最小值的位置
void ST(int l,int r)   //预处理RMQ的dp数组
{
    for(int i = l; i <= r; i++)
        dp[i][0]=i;
    for(int i = 1; (1<<i) <= r-l+1; i++){
        for(int j = l; j+(1<<i)-1 <= r; j++){
            int x = dp[j][i-1];
            int y = dp[j+(1<<(i-1))][i-1];
            dp[j][i] = dep[x]<dep[y]?x:y;
        }
    }
}
int RMQ(int l,int r)    //RMQ处理出(l,r)的最小值位置
{
    int k = 0;
    while((1<<(k+1)) <= r-l+1)
        k++;
    int x = dp[l][k];
    int y = dp[r-(1<<k)+1][k];
    return dep[x]<dep[y]?x:y;
}
int LCA(int x,int y)    //将处理出的位置对应其点
{
    int l = first[x];
    int r = first[y];
    if(l > r){int temp = l; l = r; r = temp;}   //(l,r)谁大谁小还不确定的
    int id=RMQ(l,r);
    return ver[id];
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n; i++)
            ma[i].clear();
        for(int i = 1; i < n; i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            Node temp;
            temp.to = v; temp.val = w;
            ma[u].push_back(temp);
            temp.to = u; temp.val = w;
            ma[v].push_back(temp);
        }
        memset(vis,0,sizeof(vis));
        top = 0;
        dfs(1,0,0);
        ST(1,top);
        while(m--){
            int u,v;
            scanf("%d%d",&u,&v);
            int lca = LCA(u,v);
            printf("%d\n",dir[u]+dir[v]-2*dir[lca]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值