POJ--1986--Distance Queries--LCA-RMQ

博客主要介绍了如何解决求解树上任意两点距离的问题,将问题转化为求最近公共祖先(LCA)并结合RMQ模板,强调简化数据存储方式对于降低编程复杂度的重要性,提到实际题目中方向信息并不影响解题。
摘要由CSDN通过智能技术生成

题目大意:求一棵树任意两点的距离

解题报告,设一个数组 dis[i]表示i节点到跟节点的距离,则任意两点的最短距离:dis[i,j]=dis[i]+dis[j]-2*dis[LCA(i,j)]

这个公式还是比较直接的吧

这样就转化成了一个LCA问题。召唤模板

就不吐槽WA到死的代码,换个节点存储方式就过了,简化的数据存储方式可以降低编程复杂度。

PS:此题中的东西南北的方向是不需要的

个人感觉就是1985加上个RMQ模板

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define maxn 51000
using namespace std;
vector<int> g[maxn];    //树的存储结构
vector<int> elen[maxn]; //存储边权
int r[maxn];        //RMQ问题中标记节点第一次在e数组中出现的位置
int e[maxn*2];      //RMQ问题中深搜记录边访问的节点 
int l[maxn*2];      //RMQ问题中记录节点的访问深度
int dp[maxn*2][20]; 
int res[maxn*2][20];
int ei,n,m,root,x,y;
bool vis[maxn];
int dis[maxn];      //dis[i]表示i节点到根节点的距离,此题中根节点是1

void init()
{
    int x,y,l;
    char dir[10];
    scanf("%d%d",&n,&m);
    memset(dis,0,sizeof(dis));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%s",&x,&y,&l,dir);
        g[x].push_back(y);
        elen[x].push_back(l);
        g[y].push_back(x);
        elen[y].push_back(l);
    }
}
void _dfs(int u) //深搜确定节点之间的继承关系以及以u为根节点的子树的最远叶子距离
{
    vis[u]=1;
    int v,vlen=g[u].size();
    for(int i=0;i<vlen;i++)
    {
        v=g[u][i];
        if(!vis[v])
        {
                dis[v]=dis[u]+elen[u][i];
                _dfs(v);
        }
    }
}
void rdfs(int u,int dep)
{
    vis[u]=1;
    e[ei]=u;
    l[ei]=dep;
    r[u]=ei;
    ei++;
    int v,vlen=g[u].size();
    for(int i=0; i<vlen; i++)
    {
        v=g[u][i];
        if(!vis[v])
        {
            rdfs(v,dep+1);
            e[ei]=u;
            l[ei]=dep;
            ei++;
        }
    }
}
void RMQ_init()
{
    for(int i=0; i<ei; i++)
    {
        dp[i][0]=l[i];
        res[i][0]=e[i];
    }
    for(int j=1; (1<<j)-1<ei; j++)
    {
        for(int i=0; i+(1<<j)<ei; i++)
        {
            x=dp[i][j-1];
            y=dp[i+(1<<(j-1))][j-1];
            if(x<y)
            {
                dp[i][j]=x;
                res[i][j]=res[i][j-1];
            }
            else
            {
                dp[i][j]=y;
                res[i][j]=res[i+(1<<(j-1))][j-1];
            }
        }
    }
}
int RMQ(int lx,int ly)
{
    int len=0;
    while(lx+(1<<(len+1))-1<=ly)len++;
    x=dp[lx][len];
    y=dp[ly-(1<<len)+1][len];
    if(x<y)return res[lx][len];
    else return res[ly-(1<<len)+1][len];
}
void solve()
{
    memset(vis,0,sizeof(vis));
    _dfs(1);
    memset(vis,0,sizeof(vis));
    ei=0;
    rdfs(1,1);
    RMQ_init();
    int a,b;
    int ic,ans,x,px;
    scanf("%d",&ic);
    for(int i=0;i<ic;i++)
    {
        scanf("%d%d",&a,&b);
        if(r[a]>r[b])swap(a,b);
        int tar=RMQ(r[a],r[b]);
        ans=dis[a]+dis[b]-2*dis[tar];
        printf("%d\n",ans);
    }
}
int main()
{
    freopen("1986.txt","r",stdin);
    init();
    solve();
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值