【线段树】【LCA的RMQ求法】【树上路径求交】车站

本文详细解析了一道结合线段树、倍增算法及LCA求解铁路网中最佳车站位置的复杂算法题目。通过线段树维护区间路径交集,利用倍增算法定位车站,辅以LCA确定路径交点,巧妙解决原树结构问题,为处理大规模网络路径分析提供高效解决方案。

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

在这里插入图片描述

 很好的一道题,一开始没有想到从路径来建立线段树,总想着从原树上搞,还觉得和树链剖分有点相似,后来看了题解才知道自己完全想错了。

车站
线段树+倍增+LCA。
首先车站一定在所有铁路的经过的点的交集上,所以可以用线段树求出区间路径的交集,同时维护离路径交的两个端点最远的点。找到出了最远的两个点后,那么可以倍增求出车站的位置。
具体地,线段树每个节点维护u,v,du,dv,u,v是路径交,du,dv是分别是距离u,v最远的点。合并两个区间的过程是先对两个区间的路径求路径交,然后新的du,dv一定是两个区间中四个du,dv中的两个。
对一段区间询问,先求出区间的u,v,du,dv,于是可以选择路径du->dv上中间的位置作为车站,如果中间点不在路径交集上,那么让u,v中的一个作为车站。

 上面是官方题解,说得非常清晰了,值得注意的是它以路径为基础建立了线段树,和原树没有太多关系,相当精彩。另外,路径的维护方面,想到只维护四个点,也是很不容易。
 补充一些细节部分。两个树上路径求交有个固定算法:倘若存在交,路径a的两个端点和路径b的两个端点两两求LCA,然后取四个lca中深度最大的那两个点,它们之间就是交路径。而当这两个点相同,并且深度小于两个路径之一时,则说明不存在交。(怎么证?唉,直观感受感受吧)
 另外(du,dv)是在(u,v)的两侧,并且中间位置如果在(u,v)之间,就是满足的车站。这个有些不好理解。事实上,假设(u,v)中有一点p,p到最远的点距离必然就是max(dis(p,du),dis(p,dv)),否则有其他情况的话会和du,dv的定义相矛盾。而p每向dv靠近1,则向du远离1,当然是两者平均的时候效果最好。候选点可能有一个可能有两个,最后判断哪个编号更小。
 寻找答案时注意点在二叉路径的(x,lca)还是(lca,y)上,需要用到倍增法
 用倍增的LCA的话会TLE(也可能我写得太臭)无奈之下写了RMQ版本的LCA,查询的效率基本上接近O(1)

#include <cstdio>
#include <algorithm>
#include <vector>

#define kl (k<<1)
#define kr (k<<1|1)
#define M (L+R>>1)
#define lin L,M
#define rin M+1,R
#define ept (Road){-1,-1}

using namespace std;
using Road=pair<int,int>;

struct node
{
   
   
    Road r,d;
}T[1<<18],res;

int n,m,u,v,q,o,l,r,x,d1,d2,d,ans1,ans2,lg2[200005];
vector<int> E[100005];
int dep[100005],p[100005][17];
int stn,rdfn[100005],st[200005][18];

void dfs(int x,int fa)   //倍增的预处理和LCA(RMQ)的预处理
{
   
   
    st[++stn][0]=x;
    rdfn[x]=stn;
    for(int &j:E[x])
        if(j!=fa)
        {
   
   
            dep[j]=dep[x]+1;
            p[j][0]=x;
            for(int k=1;1<<k<=dep[j];k++)
                p[j][k]=p[p[j][k-1]][k-1];
            dfs(j,x);
            st[++stn][0]=x;
        }
}

void RMQ_init()   //深搜序列中两个位置之间深度最小的那个点就是LCA
{
   
   
    for(int j=1;1<<j<=stn;j++)
        for(int i=1;1<<j<=stn-i+1;i++)
            st
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值