学了一发LCA的倍增算法+跳表维护。

先说说LCA倍增算法,思路是fa[i][j]求的是i结点的2^j倍的祖先,其中2^0就是父结点了。所以可以递推fa[i][j]=fa[fa[i][j-1]][j-1]。

当求LCA时,设深度u>v,则先倍增把u提到v的同等深度,若u==v,lca就是u,否则,两点同时倍增,直到最小深度的p[u][j]!=p[v][j],此时他们的父亲p[u][0]即lca。

可以看大牛http://www.cnblogs.com/OUSUO/p/3805715.html?utm_source=tuicool,先转一发。

1. DFS预处理出所有节点的深度和父节点
inline void dfs(int u)
{
int i;
for(i=head[u];i!=-1;i=next[i])
{
if (!deep[to[i]])
{
deep[to[i]] = deep[u]+1;
p[to[i]][0] = u; //p[x][0]保存x的父节点为u;
dfs(to[i]);
}
}
}

2. 初始各个点的2^j祖先是谁 ,其中2^j(j=0...log(该点深度))倍祖先,1倍祖先就是父亲,2倍祖先是父亲的父亲......。

void init()
{
int i,j;
//p[i][j]表示i结点的第2^j祖先
for(j=1;(1<<j)<=n;j++)
for(i=1;i<=n;i++)
if(p[i][j-1]!=-1)
p[i][j]=p[p[i][j-1]][j-1];//i的第2^j祖先就是i的第2^(j-1)祖先的第2^(j-1)祖先
}
3.从深度大的节点上升至深度小的节点同层,如果此时两节点相同直接返回此节点,即lca。
否则,利用倍增法找到最小深度的p[a][j]!=p[b][j],此时他们的父亲p[a][0]即lca。
int lca(int a,int b)//最近公共祖先
{
int i,j;
if(deep[a]<deep[b])swap(a,b);
for(i=0;(1<<i)<=deep[a];i++);
i--;
//使a,b两点的深度相同
for(j=i;j>=0;j--)
if(deep[a]-(1<<j)>=deep[b])
a=p[a][j];
if(a==b)return a;
//倍增法,每次向上进深度2^j,找到最近公共祖先的子结点
for(j=i;j>=0;j--)
{
if(p[a][j]!=-1&&p[a][j]!=p[b][j])
{
a=p[a][j];
b=p[b][j];
}
}
return p[a][0];
}
 
 
 
维护跳表的思想其实和ST算法是一样的,dp[i][j]表示区间i到i+(2^j)-1的LCA,由底往上递推就是dp[i][j]=LCA(dp[i][j-1],dp[i+(1<<j)][j-1])。即可。查询时,也按照跳表查询就可以了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std; const int N=300010; struct Edge{
int v,next;
}edge[N*2];
int head[N],tot;
int fa[N][22],dp[N][22];
int dep[N]; void addedge(int u,int v){
edge[tot].v=v;
edge[tot].next=head[u];
head[u]=tot++;
} void BFS(int rt){
queue<int>que;
que.push(rt);
fa[rt][0]=-1; dep[rt]=1;
while(!que.empty()){
int u=que.front();
que.pop();
for(int i=1;i<=20;i++){
if(fa[u][i-1]!=-1){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
}
for(int e=head[u];e!=-1;e=edge[e].next){
int v=edge[e].v;
if(dep[v]==0){
dep[v]=dep[u]+1;
fa[v][0]=u;
que.push(v);
}
}
}
} int LCA(int u,int v){
int i,j;
if(dep[u]<dep[v])swap(u,v);
for(i=0;(1<<i)<=dep[u];i++);
i--;
for(j=i;j>=0;j--){
if(dep[u]-(1<<j)>=dep[v])
u=fa[u][j];
}
if(u==v) return u;
for(j=i;j>=0;j--){
if(fa[u][j]!=-1&&fa[u][j]!=fa[v][j]){
u=fa[u][j];
v=fa[v][j];
}
}
return fa[u][0];
} int main(){
int n,q,u,v;
while(scanf("%d",&n)!=EOF){
memset(head,-1,sizeof(head));
tot=0;
memset(fa,-1,sizeof(fa));
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
memset(dep,0,sizeof(dep));
BFS(1);
// cout<<LCA(1,5)<<endl;
// cout<<LCA(2,3)<<endl;
for(int i=1;i<=n;i++)
dp[i][0]=i;
for(int j=1;j<=20;j++){
for(int i=1;i+(1<<j)-1<=n;i++)
dp[i][j]=LCA(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
/* for(int i=1;i<=n;i++){
for(int j=0;j<=6;j++)
cout<<dp[i][j]<<" ";
cout<<endl;
}*/
scanf("%d",&q);
while(q--){
scanf("%d%d",&u,&v);
// if(u>v) swap(u,v);
if(u==v)
printf("%d\n",u);
else{
int ans=u;
for(int i=20;i>=0;i--){
if(u+(1<<i)-1<=v){
ans=LCA(ans,dp[u][i]);
u=u+(1<<i);
}
}
printf("%d\n",ans);
}
}
}
return 0;
}

  

HDU 5266 bc# 43 LCA+跳表的更多相关文章

  1. HDU 5266 pog loves szh III (线段树+在线LCA转RMQ)

    题目地址:HDU 5266 这题用转RMQ求LCA的方法来做的很easy,仅仅须要找到l-r区间内的dfs序最大的和最小的就能够.那么用线段树或者RMQ维护一下区间最值就能够了.然后就是找dfs序最大 ...

  2. hdu 5266 pog loves szh III(lca + 线段树)

    I - pog loves szh III Time Limit:6000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I ...

  3. skiplist 跳表(1)

    最近学习中遇到一种新的数据结构,很实用,搬过来学习. 原文地址:skiplist 跳表   为什么选择跳表 目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等. ...

  4. SkipList跳表基本原理

    为什么选择跳表 目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等. 想象一下,给你一张草稿纸,一只笔,一个编辑器,你能立即实现一颗红黑树,或者AVL树 出来吗 ...

  5. 算法进阶面试题06——实现LFU缓存算法、计算带括号的公式、介绍和实现跳表结构

    接着第四课的内容,主要讲LFU.表达式计算和跳表 第一题 上一题实现了LRU缓存算法,LFU也是一个著名的缓存算法 自行了解之后实现LFU中的set 和 get 要求:两个方法的时间复杂度都为O(1) ...

  6. 数据结构笔记之跳表(SkipList)

    一.跳表简述 跳表可以看做是一个带有索引的链表,在介绍跳表之前先看一下一个普通的链表,假如当前维护了一个有序的链表: 现在要在这个链表中查找128,因为事先不知道链表中数值的分布情况,我们只能从前到后 ...

  7. C语言跳表(skiplist)实现

    一.简介 跳表(skiplist)是一个非常优秀的数据结构,实现简单,插入.删除.查找的复杂度均为O(logN).LevelDB的核心数据结构是用跳表实现的,redis的sorted set数据结构也 ...

  8. K:跳表

      跳表(SkipList)是一种随机化的数据结构,目前在redis和leveldb中都有用到它,它的效率和红黑树以及 AVL 树不相上下,但跳表的原理相当简单,只要你能熟练操作链表, 就能轻松实现一 ...

  9. SkipList跳表(一)基本原理

    一直听说跳表这个数据结构,说要学一下的,懒癌犯了,是该治治了 为什么选择跳表 目前经常使用的平衡数据结构有:B树.红黑树,AVL树,Splay Tree(这个树好像还没有听说过),Treep(也没有听 ...

随机推荐

  1. AcWing算法基础1.3

    二分 二分分为整数二分和实数二分,其中整数二分模板有两个 模板: 整数二分模板 第一种模板将区间分为[ l , mid ]  和 [ mid + 1, r ] int bsearch_1(int l, ...

  2. 【转】Linux下使用locale命令设置语言环境

    转自:http://www.cnblogs.com/dolphi/p/3622570.html locale命令设置语言环境 在Linux中通过locale来设置程序运行的不同语言环境,locale由 ...

  3. 大数据~说说ZooKeeper

    一些概念 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase和Kafka重要组件.它是一个为分布式应用提供一致性 ...

  4. UE4源码版食用要记

    UE4源码版和预编译版不能共享工程,这和插件版是一样的. 一般来说我都是在VS中生成编辑器,于编辑器中添加新类,VS中编辑代码. 编译引擎的时候编译配置使用的是devepolmenteditor.开发 ...

  5. FullCalendar日程设置

    顺序很重要!!!不然会报错,后面的文件会引用前面的一些东西. shili1:  http://blog.csdn.net/lizai22/article/details/53522523 shili ...

  6. sqlite学习笔记之sqlite3_open函数的使用

    作者:朱金灿 来源:http://blog.csdn.net/clever101 打开sqlite数据库需要用到sqlite3_open函数,但是sqlite3_open函数的第一个参数是数据库文件的 ...

  7. Android 从服务器获取时间戳转换为年月日

    用JAVA相关类转换.代码如下: Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(NumberUtils.ge ...

  8. 使用Hexo搭建个人博客配置全过程

    大致过程分为: 1.搭建Node.js 环境 2. 搭建Git 环境 3.安装配置Hexo 4.GitHub 注册和配置 5. 关联Hexo 与 GitHub Pages 7.Hexo的常用操作 下面 ...

  9. JS高级——扩展内置对象的方法

    基本概念 内置对象有很多,几个比较重要的:Math.String.Date.Array 基本使用 1.内置对象创建出来的对象使用的方法使用的其实都是内置对象的原型对象中的方法 (1)a并没有charA ...

  10. SQL基本操作——declare if lese while

    declare --第一种 declare @i int set @i= (select COUNT(*) from t8) select @i --第二种 declare @i int select ...