lca

noip2015day2-运输计划

流过昼夜 提交于 2019-11-27 10:49:28
题目描述 公元$ 2044 $年,人类进入了宇宙纪元。 \(L\) 国有 \(n\) 个星球,还有 \(n-1\) 条双向航道,每条航道建立在两个星球之间,这 \(n-1\) 条航道连通了 \(L\) 国的所有星球。 小 \(P\) 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 \(u_i\) 号星球沿最快的宇航路径飞行到 \(v_i\) 号星球去。显然,飞船驶过一条航道 是需要时间的,对于航道 \(j\) ,任意飞船驶过它所花费的时间为 \(t_j\) ,并且任意两艘飞船之 间不会产生任何干扰。 为了鼓励科技创新, \(L\) 国国王同意小 \(P\) 的物流公司参与 \(L\) 国的航道建设,即允许小 $P $把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。 在虫洞的建设完成前小 \(P\) 的物流公司就预接了 \(m\) 个运输计划。在虫洞建设完成后, 这 \(m\) 个运输计划会同时开始,所有飞船一起出发。当这 \(m\) 个运输计划都完成时,小 \(P\) 的 物流公司的阶段性工作就完成了。 如果小 \(P\) 可以自由选择将哪一条航道改造成虫洞,试求出小 \(P\) 的物流公司完成阶段性工作所需要的最短时间是多少? Input 第一行包括两个正整数 \(n、m\) ,表示 \(L\) 国中星球的数量及小 \(P\)

NOIP2015运输计划的一个O(n)解法

偶尔善良 提交于 2019-11-27 10:22:54
一个O(n)的解法。 不难发现有如下性质: 被改造成虫洞的边一定在最长路径上 那么,我们用类似提直径的方法把 这条路径给拎出来 就会形成这样的一棵树。 那么,对于一条边,若其被改成虫洞,最长路径会有如下三种情况: 依然是当前的最长路径 被改造边左端点的最长路径 被改造边右端点的最长路径 难道我们不用考虑经过该边的路径吗?? 当然不用。 我们已经是在 最长路径 上改造边了, 而最长路径是长于任何一条路径的 (废话),那么经过该边的任何路径在此边被改造后 依然短于最长路径 ,故不用考虑。 至于求两端的最长路径,我们可以前缀和后缀分别维护一下。 那么怎么求前缀(后缀)呢? 我们可以在每个点开个vector,对于一条路径,我们把另一端点以及路径的编号加到vector中。 然后,再开个vis数组,表示这个点是否被访问过。 在遍历时,我们访问所有以这一点为一端点的路径标号 \(id\) 和另一端点 \(y\) ,若另 \(vis[y]=true\) ,就拿 \(id\) 的长度去更新即可。(口胡不清,这最好手动模拟一下) 至于求路径长和LCA呢? 额,树剖我是当常数看的。。。 实在不行你可以Tarjan离线预处理啊 代码( 巨丑无比 ): #include<bits/stdc++.h> #define reg register int #define MAXN 300010 using

hihocoder1954 : 压缩树

百般思念 提交于 2019-11-27 09:34:24
传送门 首先求出缩一个点 $x$ 的贡献,就是缩 $x$ 的父亲的贡献加上 $x$ 的子树多减少的深度 由于缩父亲的贡献已经考虑过了,那么 $x$ 的子树多减少的深度就是子树的节点数 注意此时要满足 $x$ 不是根节点或根节点的儿子,不然缩和没缩是一样的 设这个贡献为 $sum[x]$ 然后把所有操作 $l,r,v$ 离线,遇到一个操作左端点就把 $v$ 插入 $set$,遇到右端点再取出,在过程中动态维护总贡献 考虑把每个当前加入的节点搞一个虚树 对于一次缩节点的操作,如果它不在虚树链上,只会影响 $x$ 与虚树的第一个交点的一条链,更上面的已经缩过了 设交点为 $u$, 那么贡献就是 $sum[x]-sum[u]$ 如果在原本已经虚树链上了,那么 $x$ 不会有贡献 现在问题是如何求与虚树的交点,考虑原本构造虚树的过程,把节点按 $dfn$ 排序,然后根据与前后节点的 $lca$ 确定具体连边 设前后节点为 $u,v$,如果 $LCA(u,x)=u$ 且 $LCA(v,x)=x$ 那么 $x$ 在虚树边 $(u,v)$ 上,不产生贡献 如果 $LCA(u,x)!=u$ 且 $LCA(v,x)=x$ 那么 $x$ 还是在虚树上 $v$ 到根的路径上,不产生贡献 如果 $LCA(u,x)=u$ 且 $LCA(v,x)!=x$ ,或者 $LCA(u,x)!=u$ 且 $LCA(v,x

LCA的多种求法(超详细!!!)

♀尐吖头ヾ 提交于 2019-11-27 06:07:00
倍增求LCA (1)树上倍增法 预处理 设f[x,k]表示x的2^k辈祖先,即从x向根节点走2^k步到达的节点。特别地,若该节点不存在,则令f[x,k]=0。f[x,0]就是x的父节点。可以得出 f[x][k]=f[f[x][k-1]][k-1] 。 我们可以对树进行遍历,由此得到f[x,0],再计算f数组所有值。 以上部分是预处理,时间复杂度为O(nlogn)。之后可以多次对不同的x,y计算LCA,每次询问的时间复杂度为O(logn)。 【代码实现】 预处理 void dfs(int u,int father) { Dep[u]=Dep[father]+1; for(int i=0;i<=19;i++) f[u][i+1]=f[f[u][i]][i]; for(int e=first[u],v; v=go[e],e; e=next[e]) { if(v==father) continue; f[v][0]=u; //v向上跳2^0=1就是u dfs(v,u); } } (2)基于f数组计算LCA(x,y) 分为以下几步: 1.设dep[x]表示x的深度。不妨设dep[x]≥dep[y](否则可交换x,y)。 2.用二进制拆分思想,把x向上调整到与y同一深度。具体来说,就是依次尝试从x向上走k= 2logn,…,21,20步,若到达的节点比y深,则令x=f[x,k]。 3.若此时x

最近公共祖先(LCA)的Tarjan算法

一曲冷凌霜 提交于 2019-11-27 00:50:23
最近公共祖先(LCA)问题 LCA(T,u,v):在有根树T中,询问一个距离根最远的结点x,使得x同时为结点u、v的祖先 LCA问题可以用朴素的DFS方法解决,但是时间复杂度就很高了,这里介绍一种高级一点的解决LCA问题的Tarjan算法。 Tarjan算法是由 Robert Tarjan 在1979年发现的一种高效的离线算法,也就是说,它要首先读入所有的询问(求一次LCA叫做一次询问),然后并不一定按照原来的顺序处理这些询问。 首先需要有一些预备知识: 1.基本图论 这个就不多讲了,如果有不知道的可以随便抓一本数据结构的书恶补一下。 2.并查集 并查集其实也是很简单的东西,实现的代码都不超过10行。 这里提一下并查集的概念,并查集是一种处理元素之间等价关系的数据结构,一开始我们假设元素都是分别属于一个独立的集合里的,主要支持两种操作: 合并两个不相交集合(Union) 判断两个元素是否属于同一集合(Find) 需要知道一点,就是并查集的Find操作的时间复杂度是常数级别的。 考察树 T 中所有与结点 u 有关的询问 (u, v) 对于子树 u 中的结点 v ,满足 LCA(u, v) = u 对于子树 p1 而非子树 u 中的结点 v ,满足 LCA(u, v) = p1 对于子树 p2 而非子树 p1 中的结点 v ,满足 LCA(u, v) = p2 算法DFS有根树T

pku 1330 LCA

╄→尐↘猪︶ㄣ 提交于 2019-11-27 00:50:19
第一道LCA #include < stdio.h > #include < string .h > #define N 10001 int unionset[N],visit[N]; int main() { int T,n,a,b,i; scanf( " %d " , & T); while (T -- ) { scanf( " %d " , & n); for (i = 1 ;i <= n;i ++ ) // init it { unionset[i] = i; visit[i] = 0 ; } for (i = 1 ;i < n;i ++ ) { scanf( " %d%d " , & a, & b); unionset[b] = a; } scanf( " %d%d " , & a, & b); /* find the root */ visit[a] = 1 ; a = unionset[a]; while (a != unionset[a]) { a = unionset[a]; visit[a] = 1 ; } while (b != unionset[b]) { if (visit[b]) break ; b = unionset[b]; } printf( " %d\n " ,b); } return 0 ; } 转载于:https://www.cnblogs

看到的一个很不错的分析LCA和RMQ的文章(转载,先收着)

只愿长相守 提交于 2019-11-27 00:48:39
首先请看定义 : 一、最近公共祖先 (Least Common Ancestors) 对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。 这里给出一个LCA的例子: 例一 对于T=<V,E> V={1,2,3,4,5} E={(1,2),(1,3),(3,4),(3,5)} 则有: LCA(T,5,2)=1 LCA(T,3,4)=3 LCA(T,4,5)=3 二、 RMQ 问题 (Range Minimum Query) RMQ问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在[i,j]里的最小值下标。这时一个RMQ问题的例子: 例二 对数列:5,8,1,3,6,4,9,5,7 有: RMQ(2,4)=3 RMQ(6,9)=6 然后给出两种问题各自的算法和解析 一 . RMQ 问题的 ST 算法 const int MAXN=100000+1; const int MAXF=17; const int INF=0x7FFFFFFF; //可?以?断?言?ceiil(log(MAXN,2))==MAXF inline int max(int a,int b){return

dfs序+RMQ求LCA详解

删除回忆录丶 提交于 2019-11-26 20:36:45
首先安利自己 倍增求LCA的博客 ,前置 (算不上) 知识在此。 LCA有3种求法:倍增求lca(上面qwq),树链剖分求lca( 什么时候会了树链剖分再说。 ),还有,标题。 是的你也来和我一起学习这个了qwq。 开始吧。 众所周知,每当你dfs时,你都能产生一棵dfs树,可以根据你的dfs序来构建。 such as( 丑陋的画风 ): 一个dfs的顺序。 以这个为例: 那么我们写出他的遍历顺序: 假如我们要求3,8(wtf?)的LCA, 那么我们首先写出他的bfs序: 123432565217871。 然后留意一下我们要求的两个数的位置。 12 3 4 3 2565217 8 71。 我们发现这样一个事情 : 两个数的LCA,一定在前一个数最后一次出现的位置(在bfs序中)。 感性 证明 : 对于前一个数最后一次出现的位置,他的意义就是 当前节点的子树已经遍历完了,并且正在进行回溯 !( 拍桌,划重点! )。 也就是说,他要回溯到他的祖先了,而它的祖先同样也是后一个节点的祖先,一定在后一个节点遍历前,前一个节点回溯后。 前一个节点<lca<后一个节点。 证毕。 那么,我们只要找到dfs遍历顺序中的 “前一个数最后一次出现的位置,后一个数第一次出现的位置”,这个区间取出区间最小值,即是两个节点的lca。 或许有人会说:为什么最小值一定是lca呢? 又需要证明了。

noip2019集训测试赛(三)

主宰稳场 提交于 2019-11-26 17:47:31
Problem A: string Time Limit: 5000 ms Memory Limit: 256 MB Description 给出一个长度为n的S串和一个长度为m的T串,定义Ai=S1S2...SiT1T2...TmSi+1Si+2...SN 若i=0表示T串在S串的前面,若i=n表示S串在T串的前面. 对于一个询问l,r,k,x,y,求字典序最小的Ai,若最小字典序有多个就输出最小的i,若不存在满足条件的i就输出-1.其中i满足l≤i≤r且x≤(i mod k)≤y。 Input 第一行输入字符串S和字符串T和一个整数q,表示q个询问 对于每个询问一共一行5个数l,r,k,x,y Output 一行一共q个数,表示q个答案 Sample Input abc d 4 0 3 2 0 0 0 3 1 0 0 1 2 1 0 0 0 1 3 2 2 Sample Output 2 3 2 -1 HINT 对于30%的数据1≤n,m,q≤10^3 对于100%的数据1≤n,m,q≤10^5 Solution 还没写出来。。。大概就是先用LCP排个序然后分个块查询 代码实现比较恶心,到时候再说。 Problem B: mex Time Limit: 1000 ms Memory Limit: 512 MB Description 给你一个无限长的数组,初始的时候都为0

【算法】【LCA】【tarjan】【倍增】【RMQ】

一曲冷凌霜 提交于 2019-11-26 16:46:36
呃,这个常用但是我一直不会 T a r j a n   Tarjan 算法 基于 df s ,在 dfs 的过程中,对于每个节点位置的询问做出相应的回答。   dfs 的过程中,当一棵子树被搜索完成之后,就把他和他的父亲合并成同一集合;在搜索当前子树节点的询问时,如果该询问的另一个节点已经被访问过,那么该编号的询问是被标记了的,于是直接输出当前状态下,另一个节点所在的并查集的祖先;如果另一个节点还没有被访问过,那么就做下标记,继续 dfs 。 //tarjian,边建边 边回答问题 #include<cstdio> #include<cstdlib> #include<vector> using namespace std; int n,m,rt; const int N=500003,M=500003; int ans[M]; vector <int> e[N]; struct node { int v,id; node(int vv,int ii) { v=vv,id=ii; } node(){} }; vector <node> q[N]; int fa[N]; int find(int x) { return fa[x]==0?x:fa[x]=find(fa[x]); } bool vis[N]; void dfs(int x,int f) { int sz=e[x]