LCA(Least Common Ancestors)

树上问题的一种。

朴素lca很简单啦,我就不多说了,时间复杂度n^2

1.倍增LCA

时间复杂度 nlongn+klogn

其实是一种基于朴素lca的优化方法,

朴素lca只能一层层的向上查询,而这个有一定状态压缩的想法

即每一次跳2^i层,让O(n)的查找变成O(logn)。

以上就是我对倍增lca的理解。

以洛谷P3128为例,

[USACO15DEC]最大流Max Flow

题目描述

Farmer John has installed a new system of N-1N−1 pipes to transport milk between the NN stalls in his barn (2 \leq N \leq 50,0002≤N≤50,000), conveniently numbered 1 \ldots N1…N. Each pipe connects a pair of stalls, and all stalls are connected to each-other via paths of pipes.

FJ is pumping milk between KK pairs of stalls (1 \leq K \leq 100,0001≤K≤100,000). For the iith such pair, you are told two stalls s_is​i​​ and t_it​i​​, endpoints of a path along which milk is being pumped at a unit rate. FJ is concerned that some stalls might end up overwhelmed with all the milk being pumped through them, since a stall can serve as a waypoint along many of the KKpaths along which milk is being pumped. Please help him determine the maximum amount of milk being pumped through any stall. If milk is being pumped along a path from s_is​i​​ to t_it​i​​, then it counts as being pumped through the endpoint stalls s_is​i​​ and

t_it​i​​, as well as through every stall along the path between them.

FJ给他的牛棚的N(2≤N≤50,000)个隔间之间安装了N-1根管道,隔间编号从1到N。所有隔间都被管道连通了。

FJ有K(1≤K≤100,000)条运输牛奶的路线,第i条路线从隔间si运输到隔间ti。一条运输路线会给它的两个端点处的隔间以及中间途径的所有隔间带来一个单位的运输压力,你需要计算压力最大的隔间的压力是多少。

输入输出格式

输入格式:

The first line of the input contains NN and KK.

The next N-1N−1 lines each contain two integers xx and yy (x \ne yx≠y) describing a pipe

between stalls xx and yy.

The next KK lines each contain two integers ss and tt describing the endpoint

stalls of a path through which milk is being pumped.

输出格式:

An integer specifying the maximum amount of milk pumped through any stall in the

barn.

差分数组+倍增LCA

先DFS建树,再倍增求lca,最后在求lca时维护一个sum数组(表示被遍历的次数),最后再一遍dfs求出最大被遍历次数

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
#define N 100001
int n,K;
struct node
{
int to,next;
}e[N];
int ans;
int dep[N],fa[N][],head[N],cnt,sum[N];
void add(int x,int y)
{
e[cnt].to=y;
e[cnt].next=head[x];
head[x]=cnt++;
return ;
}
void init()
{
memset(head,-,sizeof(head));
return ;
}
void dfs(int x,int from)
{
dep[x]=dep[from]+;
fa[x][]=from;
for(int i=head[x];i!=-;i=e[i].next)
{
if(e[i].to!=from)
{
dfs(e[i].to,x);
}
}
return ;
}
int lca(int x,int y)
{
if(dep[x]<dep[y])
{
swap(x,y);
}
int dep1=dep[x]-dep[y];
for(int i=;i<=;i++)
{
if((dep1&(<<i))!=)
{
x=fa[x][i];
}
}
if(x==y)
{
return x;
}
for(int i=;i>=;i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][];
}
void renew(int x)
{
for(int i=head[x];i!=-;i=e[i].next)
{
int to1=e[i].to;
if(to1!=fa[x][])
{
renew(to1);
sum[x]+=sum[to1];
}
}
ans=max(ans,sum[x]);
return ;
}
int main()
{
init();
scanf("%d%d",&n,&K);
for(int i=;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dep[]=;
dfs(,);
for(int i=;i<=;i++)
{
for(int j=;j<=n;j++)
{
fa[j][i]=fa[fa[j][i-]][i-];
}
}
for(int i=;i<=K;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int z=lca(x,y);
sum[z]--;
sum[fa[z][]]--;
sum[x]++;
sum[y]++;
}
renew();
printf("%d\n",ans);
return ;
}

明天继续更新,今天先写到这里...

2.离线tarjan_lca

时间复杂度O(n)

唯一一个lcaO(n)的优化方法...

但是实用性低,代码难写,思想复杂...

并查集+树形DP+离线思想

把查询离线,一遍dfs遍历,查出lca并储存

例题 JLOI2014松鼠的新家

题解 查分数组+tarjanlca

JLOI2014松鼠的新家

Description

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,……,最后到an,去参观新家。

可是这样会导致维尼重复走很多房间,懒惰的维尼不听地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。维尼是个馋家伙,立马就答应了。

现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

Input

第一行一个整数n,表示房间个数

第二行n个整数,依次描述a1-an

接下来n-1行,每行两个整数x,y,表示标号x和y的两个房间之间有树枝相连。

Output

一共n行,第i行输出标号为i的房间至少需要放多少个糖果,才能让维尼有糖果吃。

Sample Input

5 1 4 5 3 2 1 2 2 4 2 3 4 5

Sample Output

1 2 1 2 1

HINT

30%的数据,n<=4000

80%的数据,n<=50000

100%的数据,2<= n <=300000

3.树链剖分_lca

树剖的用处实在很多

时间复杂度:O(nlogn)

定义:
树链:就是树上的路径。
剖分:就是把路径分类为重链和轻链。
树链剖分:把一棵树剖分为若干条链,然后利用数据结构(树状数组,SBT,Splay,线段树等等)去维护每一条链,复杂度为O(logn)
重儿子:siz[v]为u的子节点中siz值最大的,那么v就是u的重儿子。
轻儿子:u的其它子节点。
重边:点u与其重儿子的连边。
轻边:点u与其轻儿子的连边。
重链:由重边连成的路径。
轻链:轻边。剖分后的树有如下性质:
性质1:如果(v,u)为轻边,则siz[v] * 2 <= siz[u];性质2:从根到某一点的路径上轻链、重链的个数都不大于logn。

那么,树链剖分的第一步当然是对树进行轻重边的划分。剖分过程分为两次dfs,或者bfs也可以。
如果是两次dfs,那么第一次dfs就是找重边,也就是记录下所有的重边。
然后第二次dfs就是连接重边形成重链,具体过程就是:以根节点为起点,沿着重边向下拓展,拉成重链,不在当前重链上的节点,都以该节点为起点向下重新拉一条重链。
剖分完毕后,每条重链相当于一段区间,然后用数据结构去维护,把所有重链首尾相接,放到数据结构上,然后维护整体。

在这里,当然有很多数组,现在我来分别介绍它们的作用:
size1[]数组,用来保存以x为根的子树节点个数
anc[]数组,用来保存当前节点的所在链的顶端节点
son[]数组,用来保存重儿子
dep[]数组,用来保存当前节点的深度
fa[]数组,用来保存当前节点的父亲

Nearest Common Ancestors

Description

给定N个节点的一棵树,有K次查询,每次查询a和b的最近公共祖先。

样例中的16和7的公共祖先(LCA:Least Common Ancestors)是4。

Input

第一行两个整数N(1 < N <= 105)、K(1 <= K <= 105)

第2~N行,每行两个整数a、b(1 <= a,b <= N),表示a是b的父亲。

第N+1~N+K+1行,每行两个整数a、b(1 <= a,b <= N),表示询问a和b的最近公共祖先是谁。

Output

输出K行,第i行表示第i个查询的最近公共祖先是谁。

Sample Input

16 1 1 14 8 5 10 16 5 9 4 6 8 4 4 10 1 13 6 15 10 11 6 7 10 2 16 3 8 1 16 12 16 7

Sample Output

4
裸的lca
注意一个要点!!
每次向上跳的时候,判断链顶端的的层数,而不是本身的层数
剩下的我只能说
树剖实在是最好写,最简单,性价比最高的lca优化算法了!
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
#define N 100005
int n,K;
struct node
{
int next,to;
}e[*N];
int head[N],dep[N],anc[N],son[N],fa[N];
int in1[N],cnt,size1[N],root;
void init()
{
memset(head,-,sizeof(head));
return ;
}
void add(int x,int y)
{
e[cnt].to=y;
e[cnt].next=head[x];
head[x]=cnt++;
return ;
}
void dfs1(int x,int from)
{
fa[x]=from;
size1[x]=;
for(int i=head[x];i!=-;i=e[i].next)
{
int to1=e[i].to;
if(to1!=from)
{
dep[to1]=dep[x]+;
dfs1(to1,x);
size1[x]+=size1[to1];
if(size1[to1]>size1[son[x]])
{
son[x]=to1;
}
}
}
return ;
}
void dfs2(int x,int top1)
{
anc[x]=top1;
if(son[x])
{
dfs2(son[x],top1);
}
for(int i=head[x];i!=-;i=e[i].next)
{
int to1=e[i].to;
if(to1!=fa[x]&&to1!=son[x])
{
dfs2(to1,to1);
}
}
return ;
}
int getlca(int x,int y)
{
int ancx,ancy;
while()
{
ancx=anc[x],ancy=anc[y];
if(ancx==ancy)break;
if(dep[ancx]<dep[ancy])
{
y=fa[ancy];
}else
{
x=fa[ancx];
}
}
if(dep[x]<dep[y])
{
return x;
}
return y;
}
int main()
{
init();
scanf("%d%d",&n,&K);
for(int i=;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
in1[y]++;
}
for(int i=;i<=n;i++)
{
if(in1[i]==)
{
root=i;
dfs1(i,);
}
}
dfs2(root,root);
for(int i=;i<=K;i++)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",getlca(x,y));
}
return ;
}

4.lca转RMQ

依据DFS序,用RMQ优化求区间最小值(dep的值)

而区间最小值即为所求

样题同上....

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
using namespace std;
#define N 200005
int n,m,in1[N],a[N*];
struct node
{
int to,next;
}e[N];
int head[N],place[N*][],dep[N*],minn[N*][],cnt,num;
void init()
{
memset(head,-,sizeof(head));
return ;
}
void add(int x,int y)
{
e[cnt].to=y;
e[cnt].next=head[x];
head[x]=cnt++;
return ;
}
void dfs(int from,int x)
{
a[x]=++num;
place[num][]=x;
dep[num]=dep[a[from]]+;
minn[num][]=dep[num];
for(int i=head[x];i!=-;i=e[i].next)
{
int to1=e[i].to;
if(to1!=from)
{
dfs(x,to1);
a[x]=++num;
dep[num]=dep[a[from]]+;
minn[num][]=dep[num];
place[num][]=x;
}
}
return ;
}
int main()
{
init();
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
in1[y]++;
}
for(int i=;i<=n;i++)
{
if(in1[i]==)
{
dfs(,i);
break;
}
}
for(int j=;j<=;j++)
{
for(int i=;i<=*n-;i++)
{
if(i+(<<j-)>n*-)break;
if(minn[i][j-]<minn[i+(<<(j-))][j-])
{
minn[i][j]=minn[i][j-];
place[i][j]=place[i][j-];
}else
{
minn[i][j]=minn[i+(<<(j-))][j-];
place[i][j]=place[i+(<<(j-))][j-];
}
}
}
for(int i=;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(a[x]>a[y])
{
swap(x,y);
}
int len=a[y]-a[x]+;
int t=(int)log2(len);
if(minn[a[x]][t]<minn[a[y]-(<<t)+][t])
{
printf("%d\n",place[a[x]][t]);
}else
{
printf("%d\n",place[a[y]-(<<t)+][t]);
}
}
return ;
}

膜拜众大佬,如有问题,请帮我指出,O(∩_∩)O谢谢!

LCA 各种神奇的LCA优化方法的更多相关文章

  1. 提升网速的路由器优化方法(UPnP、QoS、MTU、交换机模式、无线中继)

    在上一篇<为什么房间的 Wi-Fi 信号这么差>中,猫哥从微波炉.相对论.人存原理出发,介绍了影响 Wi-Fi 信号强弱的几大因素,接下来猫哥再给大家介绍几种不用升级带宽套餐也能提升网速的 ...

  2. php-fpm优化方法详解

    php-fpm优化方法 php-fpm存在两种方式,一种是直接开启指定数量的php-fpm进程,不再增加或者减少:另一种则是开始时开启一定数量的php-fpm进程,当请求量变大时,动态的增加php-f ...

  3. 30多条mysql数据库优化方法,千万级数据库记录查询轻松解决(转载)

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

  4. Android中ListView的几种常见的优化方法

    Android中的ListView应该算是布局中几种最常用的组件之一了,使用也十分方便,下面将介绍ListView几种比较常见的优化方法: 首先我们给出一个没有任何优化的Listview的Adapte ...

  5. php-fpm进程数优化方法

    原文地址:https://www.douban.com/note/315222037/ 背景最近将Wordpress迁移至阿里云.由于自己的服务器是云服务器,硬盘和内存都比较小,所以内存经常不够使,通 ...

  6. DevExpress ChartControl大数据加载时有哪些性能优化方法

    DevExpress ChartControl加载大数据量数据时的性能优化方法有哪些? 关于图表优化,可从以下几个方面解决: 1.关闭不需要的可视化的元素(如LineMarkers, Labels等) ...

  7. Tomcat从内存、并发、缓存方面优化方法

    Tomcat有很多方面,从内存.并发.缓存四个方面介绍优化方法.   一.Tomcat内存优化 Tomcat内存优化主要是对 tomcat 启动参数优化,我们可以在 tomcat 的启动脚本 cata ...

  8. 股票投资组合-前进优化方法(Walk forward optimization)

    code{white-space: pre;} pre:not([class]) { background-color: white; }if (window.hljs && docu ...

  9. Caffe学习系列(8):solver优化方法

    上文提到,到目前为止,caffe总共提供了六种优化方法: Stochastic Gradient Descent (type: "SGD"), AdaDelta (type: &q ...

随机推荐

  1. Day7 小练习(统计初始化数据的次数和对象之间的交互)

    写一个小练习,定义好一个类,每初始化一次,计数器+1,统计最后次数. class OldboyStudent: school = 'oldboy' count= def __init__(self,n ...

  2. jsonp学习

    使用 JSONP 实现跨域通信:http://www.ibm.com/developerworks/cn/web/wa-aj-jsonp1/

  3. (原创)ubuntu 10.04+ruby1.9.2+rails3 安装记录

    第一步当然是现在ruby 1.9.2 的sourcecode了,因为现在的ubuntu 源中还没有1.9.2的版本 我下载的是ruby-1.9.2-p290.tar.gz 然后解压到/usr/loca ...

  4. [ SSH框架 ] Struts2框架学习之三(OGNl和ValueStack值栈学习)

    一.OGNL概述 1.1 什么是OGNL OGNL的全称是对象图导航语言( object-graph Navigation Language),它是一种功能强大的开源表达式语言,使用这种表达式语言,可 ...

  5. MySQL技术内幕 InnoDB存储引擎(笔记)

    1. InnoDB 体系架构 其中,后台程序主要负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据. 此外将已经修改的数据刷新到磁盘文件,同时保证在数据库发生异常的时候Innodb能恢复正常 ...

  6. 杨老师课堂_Java核心技术下之控制台模拟记事本案例

    预览效果图: 背景介绍: 编写一个模拟记事本的程序通过在控制台输入指令,实现在本地新建文件打开文件和修改文件等功能. 要求在程序中: 用户输入指令1代表"新建文件",此时可以从控制 ...

  7. Java开源生鲜电商平台-购物车模块的设计与架构(源码可下载)

    ava开源生鲜电商平台-购物车模块的设计与架构(源码可下载) 说明:任何一个电商无论是B2C还是B2B都有一个购物车模块,其中最重要的原因就是客户需要的东西放在一起,形成一个购物清单,确认是否有问题, ...

  8. Springboot 框架学习

    Springboot 框架学习 前言 Spring Boot是Spring 官方的顶级项目之一,她的其他小伙伴还有Spring Cloud.Spring Framework.Spring Data等等 ...

  9. HTTP协议GET HEAD简单介绍

    一.HTTP协议简介 超文本传输协议(Hypertext Transfer Protocol,简称HTTP)是应用层协议,自 1990 年起,HTTP 就已经被应用于 WWW 全球信息服务系统. HT ...

  10. 【转】Java线程面试题 Top 50

    不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员的欢迎.大多数待遇丰厚的Java开发职位都要求开发者精通多线程 ...