HDU 2586 How far away ?(经典)(RMQ + 在线ST+ Tarjan离线) 【LCA】
<题目链接>
题目大意:
给你一棵带有边权的树,然后进行q次查询,每次查询输出指定两个节点之间的距离。
解题分析:
本题有多重解决方法,首先,可用最短路轻易求解。若只用LCA解决本题,也有三种常用的做法,具体方法如下:
LCA转RMQ解法:
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
;
int dis[M],dep[M],first[M],pos[M];
];
int cnt;
struct Edge{
int to,w;
};
vector<Edge>G[M];
int Min(int a,int b){ //得到深度更小的节点的序号,即更上层的节点
return dep[a]<dep[b]?a:b;
}
void RMQ_init(int n){ //预处理ST表,得到从[i,i+2^j-1]这个区间深度最小的节点的序号,注意,区间的下标代表遍历到的顺序
;i<=n;i++)
st[i][]=i;
;(<<j)<=n;j++)
;i+(<<j)-<=n;i++)
st[i][j]=Min(st[i][j-],st[i+(<<(j-))][j-]);
}
int RMQ(int l,int r){ //得到指定区间内深度最小的节点的编号,注意这里的l和r指的是按dfs顺序遍历的时间序号
))/log(2.0);
<<k)+][k]);
}
int get_LCA(int a,int b){ //在两点遍历顺序的区间内选择深度最小的节点的下标
return first[a]<first[b]?pos[RMQ(first[a],first[b])]:pos[RMQ(first[b],first[a])];
}
void dfs(int u,int fa,int deep){
dep[cnt]=deep,pos[cnt]=u,first[u]=cnt++; //表示cnt时间遍历到的节点的深度,和cnt时间遍历到的节点的序号,和第一次遍历到u节点的时间
;i<G[u].size();i++){
int v=G[u][i].to;
if(v==fa)continue;
dis[v]=dis[u]+G[u][i].w; //更新子节点距离
dfs(v,u,deep+);
pos[cnt]=u; //在dfs搜索完u的所有子节点后,回溯到u
dep[cnt++]=deep; //u的深度仍然为当前的深度
}
}
int main(){
int T,n,m;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
cnt=;
;i<M;i++)
G[i].clear();
;i<n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
G[a].push_back(Edge{b,c}); //建无向边
G[b].push_back(Edge{a,c});
}
dfs(,-,);
RMQ_init(*n-);
while(m--){
int a,b;
scanf("%d%d",&a,&b);
printf(*dis[get_LCA(a,b)]);
}
}
;
}
在线ST解法可以看这篇博客 >>>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
;
struct node{
int to,w,next;
}e[maxn*];
],n,cnt,head[maxn];
void add(int u,int v,int w){
e[cnt].to=v,e[cnt].w=w;
e[cnt].next=head[u],head[u]=cnt++;
}
void dfs(int u,int pre,int t){
dep[u]=t;//深度
f[u]=pre;//父节点
;i=e[i].next){
int v=e[i].to;
if(v!=pre){
dis[v]=dis[u]+e[i].w;//距离跟的距离
dfs(v,u,t+);
}
}
}
void init()
{
//p[i][j]表示i结点的第2^j祖先
int i,j;
;(<<j)<=n;j++)
;i<=n;i++)
p[i][j]=-;
;i<=n;i++)p[i][]=f[i];
;(<<j)<=n;j++)
;i<=n;i++)
]!=-)
p[i][j]=p[p[i][j-]][j-];//i的第2^j祖先就是i的第2^(j-1)祖先的第2^(j-1)祖先
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y); //令x为深度更深的节点
int d=dep[x]-dep[y];
;(d>>i)!=;i++)
)x=p[x][i]; //以上的操作是处理较深的节点,使两节点深度一致
if(x==y)return x; //如果深度一致时,两节点相同,那么直接返回即可
;i>=;i--)
if(p[x][i]!=p[y][i]){ //跳2^i步不一样,就跳,否则不跳
x=p[x][i];
y=p[y][i];
}
]; //上述操作做完,两点的LCA都在上一层,所以再走一步即可
}
int main(){
int T;
scanf("%d",&T);
while(T--){
int m,i,a,b,c,ans;
scanf("%d%d",&n,&m);
memset(head,-,sizeof(head));
cnt=;
;i<n;i++){
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
dis[]=;
dfs(,-,);//找到各点的深度和各点的父节点以及距离根的距离
init(); //初始各个点的2^j祖先是谁
;i<m;i++){
scanf("%d%d",&a,&b);
ans=dis[a]+dis[b]-*dis[LCA(a,b)];
printf("%d\n",ans);
}
}
;
}
离线Tarjan算法可以看这篇博客 >>> ,还有这篇 >>>
#include<iostream>
#include<cstdio>
#include<cstring>
#define Maxn 40010
#define Maxm 210
using namespace std;
struct edge{
int fr,to,w,lca,next;
}p[Maxn<<],ask[Maxm<<];
int head[Maxn],ah[Maxn];
int tot,tot1;
void addedge(int a,int b,int c){
p[tot].to=b;
p[tot].w=c;
p[tot].next=head[a];
head[a]=tot++;
}
void addedge1(int a,int b){
ask[tot1].fr=a;
ask[tot1].to=b;
ask[tot1].next=ah[a];
ah[a]=tot1++;
}
int fa[Maxn];
int findset(int x){ //寻找根节点
return fa[x]==x?x:(fa[x]=findset(fa[x]));
}
void unionset(int a,int b){ //将a、b的根节点链接
fa[findset(a)]=findset(b);
}
int vis[Maxn];
int anc[Maxn];
int dist[Maxn];
void LCA(int u,int fa){
;i=p[i].next){
int v=p[i].to;
if(fa!=v){
dist[v]=dist[u]+p[i].w;
LCA(v,u);
unionset(u,v); //将子树合并到父亲
anc[findset(u)]=u; //维护新集合指向父亲
}
}
vis[u]=; //设置已访问
;i=ask[i].next){ //处理与u关联的边
int v=ask[i].to;
if(vis[v]) //若v已访问,则说明u,v的lca是v所在集合的指向
ask[i].lca=ask[i^].lca=anc[findset(v)];
}
}
void init(int n){
tot=tot1=;
memset(head,-,sizeof head);
memset(ah,-,sizeof ah);
memset(vis,,sizeof vis);
;i<=n;i++) fa[i]=i;
}
int main()
{
int t,n,m,a,b,c;
cin>>t;
while(t--){
cin>>n>>m;
init(n);
;i<n;i++){
scanf("%d%d%d",&a,&b,&c);
addedge(a,b,c);
addedge(b,a,c);
}
;i<=m;i++){ //把全部的query也建一颗无向树,进行离线处理
scanf("%d%d",&a,&b);
addedge1(a,b);
addedge1(b,a);
}
dist[]=;
LCA(,-);
;i<tot1;i+=)
printf(*dist[ask[i].lca]);
}
;
}
2018-10-20
HDU 2586 How far away ?(经典)(RMQ + 在线ST+ Tarjan离线) 【LCA】的更多相关文章
- HDU 4757 Tree(可持久化Trie+Tarjan离线LCA)
Tree Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 102400/102400 K (Java/Others) Total Su ...
- hdu 2586(LCA在线ST)
How far away ? Time Limit: / MS (Java/Others) Memory Limit: / K (Java/Others) Total Submission(s): A ...
- HDU 2586 How far away ?(LCA模板 近期公共祖先啊)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the vi ...
- hdu2586(lca模板 / tarjan离线 + RMQ在线)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 题意: 给出一棵 n 个节点的带边权的树, 有 m 个形如 x y 的询问, 要求输出所有 x, ...
- HDU 2586 (LCA模板题)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2586 题目大意:在一个无向树上,求一条链权和. 解题思路: 0 | 1 / \ 2 3 ...
- HDU 2586 LCA
题目大意: 多点形成一棵树,树上边有权值,给出一堆询问,求出每个询问中两个点的距离 这里求两个点的距离可以直接理解为求出两个点到根节点的权值之和,再减去2倍的最近公共祖先到根节点的距离 这是自己第一道 ...
- HDU - 2586 How far away ?(LCA模板题)
HDU - 2586 How far away ? Time Limit: 1000MS Memory Limit: 32768KB 64bit IO Format: %I64d & ...
- hdu 2586 How far away ?倍增LCA
hdu 2586 How far away ?倍增LCA 题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2586 思路: 针对询问次数多的时候,采取倍增 ...
- LCA(最近公共祖先)--tarjan离线算法 hdu 2586
HDU 2586 How far away ? Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/ ...
随机推荐
- 使用JUnit进行类的测试(一)
首先是测试的一些常用标注: @Test:执行测试的方法 @Before & @After : 在 测试的方法 “前” 或者 “后” 被唤醒 -Initialization -Release r ...
- 使用open live writer客户端写博客
注:Windows Live Writer 已经停止更新,建议安装 Open Live Writer,下载地址: http://openlivewriter.org/ 使用open live writ ...
- 在Ubuntu 15下搭建V/P/N服务器pptpd安装和配置
在Ubuntu 15下搭建VPN服务器pptpd安装和配置 在ubuntu下配置vpn的方式有很多种,其中比较常见的是pptpd,它配置简单,但是安全性不高,不过对于一般使用来说足够了,我按照程搭建了 ...
- Oracle+PL+SQL从入门到精通.丁士锋.清华大学出版社.2012
\t第1篇 pl/sql开发入门第1章 oracle 11g数据库系统1.1 关系型数据库系统介绍1.1.1 什么是关系型数据模型1.1.2 数据库系统范式1.1.3 关系型数据库管理系统1.1.4 ...
- Confluence 6 查看系统信息
系统信息界面提供了有关 Confluence 的配置信息和 Confluence 部署的环境信息. 希望对你的系统信息进行查看: 在屏幕的右上角单击 控制台按钮 ,然后选择 General Confi ...
- Confluence 6 workbox 配置查询间隔
查询间隔在Confluence 服务器中的 workbox 被用来显示应用内通知和任务. 激活的查询间隔(Active polling interval) Confluence 将会等待多少时间(秒) ...
- Python编程:从入门到实践(选记)
本文参考< Python 编程:从入门到实践>一书,作者: [ 美 ] Eric Matthes 第1章 起步 1.1 搭建python环境 在不同的操作系统中, Python 存 ...
- Python基础之继承与派生
一.什么是继承: 继承是一种创建新的类的方式,新建的类可以继承一个或过个父类,原始类成为基类或超类,新建的类则称为派生类 或子类. 其中,继承又分为:单继承和多继承. class parent_cla ...
- python(4): regular expression正则表达式/re库/爬虫基础
python 获取网络数据也很方便 抓取 requests 第三方库适合做中小型网络爬虫的开发, 大型的爬虫需要用到 scrapy 框架 解析 BeautifulSoup 库, re 模块 (一) r ...
- python 内置数据类型之数字
目录: 1.2. 数字 1.2.1. 数字类型 1.2.2. 浮点数 1.2.3. 进制记数 1.2.4. 设置小数精度 1.2.5. 分数 1.2.6. 除法 1.2 数字 1.2.1 数字类型 ...