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/ ...
随机推荐
- swift 学习- 27 -- 访问控制
// 访问控制 可以限定其源文件 或模块中的代码对你的代码的访问级别, 这个特性可以让我们隐藏代码的一些实现细节, 并且可以为其他人可以访问和使用的代码提供接口 // 你可以明确地给某个类型 (类, ...
- Confluence 6 后台中为站点添加应用导航
Confluence 6 后台中为站点添加应用导航的连界面和方法. https://www.cwiki.us/display/CONFLUENCEWIKI/Configuring+the+Site+H ...
- python之vscode中手动选择python解释器(mac)
要选择特定的解释器,请从命令选项板(⇧⌘P)调用Python:Select Interpreter命令. 更详细请看:http://www.cnblogs.com/it-tsz/p/9312151.h ...
- day11 函数的位置形参,位置实参,可变长位置形参,关键字形参
今天内容 函数的参数详解 形参与实参 形参及形式参数,就是在定义函数是括号中指定的参数(本质就是一个名字) 实参及实际参数,指的是在调用函数是传入的参数)(本质就是一个值) 在调用函数是就会把形参和实 ...
- sql查询条件为空的另类写法o( ̄▽ ̄)d
简单描述:今天看老大提交的代码,发现了一个有意思的事情,一条sql中判断条件是空,老大的写法,让我眼前一亮.直接上代码 代码: <select id="getxxxs" re ...
- mysql 去除重复 Select中DISTINCT关键字的用法 在使用mysql时,有时需要查询出某个字段不重复的记录,虽然mysql提供 有distinct这个关键字来过滤掉多余的重复记录只保留一条,但往往只用它来返回不重复记录的条数,而不是用它来返回不重记录的所有值。其原因是 distinct只能返回它的目标字段,而无法返回其它字段,这个问题让我困扰了很久,用distinct不能解决的话,
在使用mysql时,有时需要查询出某个字段不重复的记录,虽然mysql提供 有distinct这个关键字来过滤掉多余的重复记录只保留一条,但往往只用它来返回不重复记录的条数,而不是用它来返回不重记 ...
- javascript 面向对象-面试题实例
/ 从设计到模式 // 设计模式简介 // 设计 // 模式 // 分开 // 从设计到模式 // 23种设计模式 // 创建型 // 工厂模式(工厂方法模式,抽象工厂模式,建造者模式) // 单例模 ...
- Nginx限制下载速度
http { limit_conn_zone $binary_remote_addr zone=one:10m; #容器共使用10M的内存来对于IP传输开销 server { lis ...
- 使用Vmware CLI 6.5控制虚拟机,并做快照
1.下载PowerCLI 6.5 http://7dx.pc6.com/wwb5/VMwarePowerCLI65.zip 2. 打开 VMware vSphere PowerCLI 出现 无法加载文 ...
- Git强制拉取覆盖本地
1.多条执行 git fetch --all git reset --hard origin/master git pull 2.单条执行 git fetch --all && git ...