学了一发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. Akka源码分析-FSM

    akka还有一个不常使用.但我觉得比较方便的一个模块,那就是FSM(有限状态机).我们知道了akka中Actor模型的具体实现之后,就会发现,Akka的actor可以非常方便的实现FSM.其实在akk ...

  2. IDEA报错,注解标红,提示Cannot resolve symbol xxx

    一般都是jar包没导进来,可以先看一下setting里maven配置的路径对不对

  3. cookie和seesion区别

    cookie 和session 的区别详解 这些都是基础知识,不过有必要做深入了解.先简单介绍一下. 二者的定义: 当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cookie ...

  4. ACM_蛋糕小王子铁头娃

    Problem Description: 铁头娃制作了很多蛋糕,想分给他的小伙伴们,他的小伙伴很喜欢铁头娃做的蛋糕,每个人都想分到最多蛋糕 铁头娃想到了一个头铁主意:先给小伙伴们从1到N编号,在1-N ...

  5. python导入包出错:ImportError: No module named XXXXX

    python中,每个py文件被称之为模块,每个具有__init__.py文件的目录被称为包.只要模块或者包所在的目录在sys.path中,就可以使用import 模块或import 包来使用. 如果想 ...

  6. Emoji过滤

    private static boolean isNotEmojiCharacter(char codePoint) { return (codePoint == 0x0) || (codePoint ...

  7. 简述UIDatePicker的用法

    1.Locale 设置DatePicker的地区,即设置DatePicker显示的语言. 1.跟踪所有可用的地区,取出想要的地区 NSLog(@"%@", [NSLocale av ...

  8. Xml的读取

    using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace WebAp ...

  9. JavaScript中的 函数splice() 的使用。

    大二接触JavaScript初期,学习函数中有一道题: 定义一个2个参数的函数.第1个参数是一个数组,第2个参数是需要删除的元素.函数功能,在第1个实参数组中查找第2个实参提供的值,找到则删除该元素( ...

  10. hibernate 级联删除报更新失败的问题(org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update)

    首先hibernate级联删除的前提是,首先需要在映射文件中配置,配置多表之间的关联关系: 下面以部门表(Dept)和员工表(Emp)为例: 1.在Emp.hbm.xml映射文件中配置many-to- ...