LCA(倍增在线算法) codevs 2370 小机房的树
codevs 2370 小机房的树
时间限制: 1 s
小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上。有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花费太多精力。已知从某个节点爬到其父亲节点要花费 c 的能量(从父亲节点爬到此节点也相同),他们想找出一条花费精力最短的路,以使得搞基的时候精力旺盛,他们找到你要你设计一个程序来找到这条路,要求你告诉他们最少需要花费多少精力
第一行一个n,接下来n-1行每一行有三个整数u,v, c 。表示节点 u 爬到节点 v 需要花费 c 的精力。
第n+1行有一个整数m表示有m次询问。接下来m行每一行有两个整数 u ,v 表示两只虫子所在的节点
一共有m行,每一行一个整数,表示对于该次询问所得出的最短距离。
3
1 0 1
2 0 1
3
1 0
2 0
1 2
1
1
2
1<=n<=50000, 1<=m<=75000, 0<=c<=1000
分类标签 Tags 点此展开
最近公共祖先 LCA 倍增法
【简介】
解决LCA问题的倍增法是一种基于倍增思想的在线算法。
【原理】
原理和同样是使用倍增思想的RMQ-ST 算法类似,比较简单,想清楚后很容易实现。
对于每个节点u , ancestors[u][k] 表示 u 的第2k个祖先是谁。很容易就想到递推式: ancestors[j][i] = ancestors[ancestors[j][i - 1]][i - 1]; 根据二进制原理,理论上 u 的所有祖先都可以根据ancestors数组多次跳转得到,这样就间接地记录了每个节点的祖先信息。
查询LCA(u,v)的时候:
(一)u和v所在的树的层数如果一样,令u'=u。否则需要平衡操作(假设u更深),先找到u的一个祖先u', 使得u'的层数和v一样,此时LCA(u,v)=LCA(u',v) 。证明很简单:如果LCA(u,v)=v , 那么u'一定等于v ;如果LCA(u,v)=k ,k!=v ,那么k 的深度一定小于 v , u、u'、v 一定在k的子树中;综上所述,LCA(u,v)=LCA(u',v)一定成立。
(二)此时u' 和 v 的祖先序列中一开始的部分一定有所重叠,重叠部分的最后一个元素(也就是深度最深,与u'、v最近的元素)就是所求的LCA(u,v)。这里ancestors数组就可以派上用场了。找到第一个不重叠的节点k,LCA(u,v)=ancestors[k][0] 。 找k的过程利用二进制贪心思想,先尽可能跳到最上层的祖先,如果两祖先相等,说明完全可以跳小点,跳的距离除2,这样一步步跳下去一定可以找到k。
. DFS预处理出所有节点的深度和父节点 inline void dfs(int u)
{
int i;
for(i=head[u];i!=-;i=next[i])
{
if (!deep[to[i]])
{
deep[to[i]] = deep[u]+;
p[to[i]][] = u; //p[x][0]保存x的父节点为u;
dfs(to[i]);
}
}
}
. 初始各个点的2^j祖先是谁 ,其中 ^j (j =...log(该点深度))倍祖先,1倍祖先就是父亲,2倍祖先是父亲的父亲......。 void init()
{
int i,j;
//p[i][j]表示i结点的第2^j祖先
for(j=;(<<j)<=n;j++)
for(i=;i<=n;i++)
if(p[i][j-]!=-)
p[i][j]=p[p[i][j-]][j-];//i的第2^j祖先就是i的第2^(j-1)祖先的第2^(j-1)祖先
}
.从深度大的节点上升至深度小的节点同层,如果此时两节点相同直接返回此节点,即lca。 否则,利用倍增法找到最小深度的 p[a][j]!=p[b][j],此时他们的父亲p[a][]即lca。 int lca(int a,int b)//最近公共祖先
{
int i,j;
if(deep[a]<deep[b])swap(a,b);
for(i=;(<<i)<=deep[a];i++);
i--;
//使a,b两点的深度相同
for(j=i;j>=;j--)
if(deep[a]-(<<j)>=deep[b])
a=p[a][j];
if(a==b)return a;
//倍增法,每次向上进深度2^j,找到最近公共祖先的子结点
for(j=i;j>=;j--)
{
if(p[a][j]!=-&&p[a][j]!=p[b][j])
{
a=p[a][j];
b=p[b][j];
}
}
return p[a][];
}
附上题解:
#define N 50100
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#define L 17
struct Edge{
int v,last,c;
}edge[N*];
int head[N],p[N][L];
int deep[N]={};
int root[N]={};
long long dis[N]={};
int n,m,u,v,c,t=;
void add_edge(int u,int v,int w)
{
++t;
edge[t].v=v;/*建边*/
edge[t].c=w;
edge[t].last=head[u];
head[u]=t;
//root[u]++;
}
void input()
{
scanf("%d",&n);
for(int i=;i<n;++i)
{
scanf("%d%d%d",&u,&v,&c);
add_edge(u,v,c);
add_edge(v,u,c);
}
memset(p,-,sizeof(p));/*因为节点编号是从0开始的,所以把祖先不存在,设为-1*/
}
void dfs(int u,long long di)
{
dis[u]=di;/*统计u到根节点的距离*/
for(int l=head[u];l;l=edge[l].last)
{
if(!deep[edge[l].v])
{
deep[edge[l].v]=deep[u]+;/*处理孩子的深度*/
p[edge[l].v][]=u;/*初始化p数组*/
dfs(edge[l].v,di+edge[l].c);
}
}
}
void init()
{
int i,j;
for(j=;(<<j)<n;j++)
for(int i=;i<n;++i)
if(p[i][j]=-)
p[i][j]=p[p[i][j-]][j-];/*DP处理出i的所有2^j祖先是谁*/
}
int lca(int a,int b)/*求最近公共祖先*/
{
int i,j;
if(deep[a]<deep[b]) swap(a,b);
for(i=;(<<i)<=deep[a];++i);
i--;/*i为估计a到根节点的最远距离,下边的平衡操作,跳点从i开始,一定可以实现*/
for(j=i;j>=;--j)
if(deep[a]-deep[b]>=(<<j))/*倍增缩短a与b之间的距离*/
a=p[a][j];
if(a==b) return a;/*当a和b到了同一深度的时候,判断是否已经相同了*/
for(int j=i;j>=;--j)
{
if(p[a][j]!=-&&p[a][j]!=p[b][j])
{
a=p[a][j];/*最终的a是lca的子节点*/
b=p[b][j];
} }/*先大步大步的蹦,每蹦一步,路程减少,下次蹦前一次的一半,直到蹦不了了,就是答案*/
return p[a][];
}
/*当a有祖先,并且a,b的祖先不相同的时候,(我们想要寻找的就是lca的子节点,也就是最小深度的p[a][j]!=p[b][j]),根据二进制原理,一定可以通过各种组合走到每一个祖先*/
int main()
{
input();
dfs(,);/*题目意思是0为根节点*/
/*for(int i=0;i<n;++i)
{
if(root[i]==2)
{
dfs(i,0);/*如果是一棵二叉树,可以统计出度为2的点是根节点*/
break;
}
}*/
init();
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&u,&v);
int zu=lca(u,v);/*在线算法,可以按照顺序查询*/
cout<<dis[u]+dis[v]-*dis[zu]<<endl;/*求最近距离的公式*/
}
return ;
}
LCA(倍增在线算法) codevs 2370 小机房的树的更多相关文章
- CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先)
CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先) 题意分析 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天, ...
- Codevs 2370 小机房的树
2370 小机房的树 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 传送门 题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为 ...
- codevs——2370 小机房的树
2370 小机房的树 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description 小机房有棵焕狗种的树,树上有N个 ...
- Codevs 2370 小机房的树 LCA 树上倍增
题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子, ...
- 放一道比较基础的LCA 的题目把 :CODEVS 2370 小机房的树
题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子, ...
- codevs 2370 小机房的树(LCA)
过了这么长的时间终于开始看LCA了... 有一次训练题卡在LCA当时不会...拖了好久好久...其实现在还是不会... 只会tarjan... 传送门 板子题咯 tarjan的算法就是基于先序遍历的顺 ...
- 【codevs2370】小机房的树 LCA 倍增
2370 小机房的树 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0 ...
- 小机房的树 codevs 2370
2370 小机房的树 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题解 查看运行结果 题目描述 Description 小机房有棵焕狗种的树 ...
- codevs2370 小机房的树 x
2370 小机房的树 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号 ...
随机推荐
- yii2-widget-fileinput英文文档翻译
源地址:http://plugins.krajee.com/file-input 该插件是为bootstrap开发的增强版h5文件上传插件,具有多文件预览,多文件选择等功能.该插件提供了基于boots ...
- linux下subversion的安装
第一章 安装 这里以RHEL5下安装subversion-1.6.6,为例 1. 下载源码包 在http://archive.apache.org/dist/subversion/网站下载 subve ...
- DNSLOG在渗透测试中的玩法儿
首先了解一下DNS是啥??? DNS(Domain Name System,域名系统),万维网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读 ...
- 对RSA的认识
#没有经过专业老师的指导,所以您在阅读本文时建议参考即可. 学习视屏:https://www.youtube.com/watch?v=TqX0AHHwRYQ https://www.youtub ...
- Xcode 9安装
Xcode 9 Xcode 9 Compatibility Xcode 9 requires a Mac running macOS 10.13.2 or later. Xcode 9 include ...
- 001使用smokeping监控idc机房网络质量情况
最近工作比较忙,也没有时间写博客,看到好友芮峰云最近一直在写博客,所以也手痒了,就先把之前的一些积累下来的文章分享给大家. 本文是介绍如何的使用smokeping来监控idc机房的网络质量情况,从监控 ...
- html 简单学习
通过记事本,依照以下四步来创建您的第一张网页. 步骤一:启动记事本 如何启动记事本: 开始 所有程序 附件 记事本 步骤二:用记事本来编辑 HTML 在记事本 ...
- 删除/添加/调用WordPress用户个人资料的联系信息
如果你要折腾主题或者将WordPress站点开放注册,你可能需要自定义WordPress用户个人资料信息.下面倡萌将简单说一下如何删除.添加和调用自定义用户信息字段. 添加或删除字段,可以在主题的 f ...
- GUC-2 原子性
import java.util.concurrent.atomic.AtomicInteger; /* * 一.i++ 的原子性问题:i++ 的操作实际上分为三个步骤“读-改-写” * int i ...
- thinkphp5 返回数组提示variable type error: array
浏览器访问控制器函数,而函数返回的是数组: function timeArr(){ $time = array(); for($i=1;$i<=7;$i++){ $d= date('d',Tim ...