lca

2019.8.19刷题统计

拟墨画扇 提交于 2019-11-27 22:11:21
今天是连续打卡的第47天。 第一题:1153 这道题是LCA模板题,没什么好说的。 AC代码: 第二题:1155 这道题需要求6次LCA(三个点的LCA需要求两次,这样求三遍),并在LCA计算中求出最小值,最后输出最小值。 这道题的运行时间超过了1秒,但由于时限是2秒,也可以过。 AC代码: 明天做树上区间查询的题,如果有空闲时间会做Tarjan求割点、割边。 来源: https://www.cnblogs.com/wangximing/p/11379959.html

BZOJ1832: [AHOI2008]聚会(LCA)

喜夏-厌秋 提交于 2019-11-27 21:51:45
题目: 1832: [AHOI2008]聚会 解析: 偶尔做做水题挺爽的 两两之间先求出LCA,发现至少有两个LCA是相同的,这个重复LCA也是深度最浅的那个,那我们就选择那个不重复的LCA,因为若选这个重复的LCA的话,这个重复的LCA到另一个LCA的路径会走两遍,反之只会走一遍 三点间的距离就是 \(dis[x] + dis[y] + dis[z] - dis[LCA(x, y)]- dis[LCA(x, z)]- dis[LCA(y, z)]\) 代码: #include <bits/stdc++.h> using namespace std; const int N = 1e6 + 10; int n, m, num; int head[N], dep[N], f[N], size[N], top[N], son[N]; struct node { int v, nx; } e[N]; template<class T>inline void read(T &x) { x = 0; int f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); x

LCA详解

a 夏天 提交于 2019-11-27 21:02:14
LCA,即最近公共祖先,在图论中应用比较广泛。 LCA的定义如下:给定一个有根树,若节点$z$同时是节点$x$和节点$y$的祖先,则称$z$是$x,y$的公共祖先;在$x,y$的所有公共祖先当中深度最大的称为$x,y$的最近公共祖先。下面给出三个最近公共祖先的例子: 显然,从上面的例子可以得出,$LCA(x,y)$即为$x,y$到根节点的路径的交汇点,也是$x$到$y$的路径上深度最小的节点。 求LCA的方法通常有三种: 向上标记法 树上倍增法 Tarjan算法 当然,求LCA还有其它方法,例如树剖等,请读者自行了解,本文主要讲解上面提到的三种方法。 向上标记法 向上标记法是求LCA最直接的方法,直接根据定义来求,单次查询的时间复杂度最坏为$O(n)$ (看起来好像还挺快的,不过什么题会只有一次查询呢) 算法流程: 从x节点向上一直走到根节点,沿途标记经过的节点 从y节点向上一直走到根节点,当第一次遇到已标记的节点时,该节点就是$LCA(x,y)$ 该方法思想是绝对简单的,实现也简单,因此在这里就不给出具体实现了。不过由于其时间复杂度过高,在实际中基本不会应用到,在这里提一下主要还是为讲解Tarjan算法做基础。 树上倍增法 树上倍增法应用非常广泛,读者可以深入地学习。用树上倍增法求LCA的时间复杂度为$O((n+m)logn)$。 树上倍增法用到了二进制拆分的思想。在求LCA时

LCA算法_普通做法&倍增&Tarjan&RMQ

末鹿安然 提交于 2019-11-27 16:36:23
题目:POJ-1330 题目大意:给你一棵含有n个结点的树,n-1条边,问两个结点的最近公共祖先是哪个节点。 普通做法 思路:让两个结点到达同一深度,再一起往上走,到达同一结点即为最近公共祖先。 例:3和7,让3往上走到16,与7同一深度后,再一起一步步往上走,到达4时,为同一个结点,即为其最近公共祖先。 基本步骤:1、邻接表存图(这里用到的是vector) 2、dfs出各个结点的深度 3、让深度较高的往上走,走到同一深度 4、两个结点一起往上走,走到同一个结点并输出 1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 using namespace std; 5 6 const int maxn=10005; 7 vector<int> son[maxn]; 8 int n; 9 int p[maxn],in[maxn],dep[maxn]; 10 11 //dfs获取结点深度 12 void dfs(int root,int depth){ 13 dep[root]=depth; 14 for(int i=0;i<son[root].size();i++){ 15 p[son[root][i]]=root; 16 dfs(son[root][i],depth+1); 17 } 18 return; 19 }

[NOIP2016]天天爱跑步(桶)

六月ゝ 毕业季﹏ 提交于 2019-11-27 16:32:50
题意 给定一颗边权为1的树,点权为v,给定m条从s到t的路径,对于每个点,求 \(ans_i=\Sigma_{j=1}^m [dist(s_j,i)==v[i]]\) 思路 有两种可能的做法,一种是把路径全加进去,再每一个点求 \(ans\) ,另一种是一条一条加路径,每次求贡献。而这道题用的是第一种 对于每一条路径,将 \(s->t\) 的路径拆分为 \(s->lca->t\) , \(s->lca\) 为左路径, \(lca->t\) 为右路径,对于左路径上的点 \(i\) ,左路径对它有贡献当且仅当 \(dep[s]-dep[i]==v[i]\) ,将 \(i\) 项移动到一边,就有 \(dep[s]==dep[i]+v[i]\) ,这样就可以把路径全部加进去再求 \(i\) 点的贡献。右路径同理有 \(dep[s]-2*dep[lca]==v[i]-dep[i]\) \(dfs\) 整颗树,从下向上回溯时求 \(ans\) ,对于回溯时遇到的点 \(i\) ,加入以它为 \(s/t\) 的左右路径,求一次 \(ans\) ,退出时减去以它为 \(lca\) 的左右路径即可, 然后就发现过不了样例 (假设当前在以 \(u\) 为根的子树中 \(dfs\) )因为在 \(u\) 的某一珂子树 \(v\) 中的时候, \(u\) 的其他子树里面的路径也被记录下来了

[NOIp 2018]all

你。 提交于 2019-11-27 15:38:47
Description 题库链接: Day1 T1 铺设道路 Day1 T2 货币系统 Day1 T3 赛道修建 Day2 T1 旅行 Day2 T2 填数游戏 Day2 T3 保卫王国 Solution 题解在这儿 Code Day1 T1 铺设道路 #include <bits/stdc++.h> using namespace std; int n, a, la, ans; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a); if (a > la) ans += a-la; la = a; } printf("%d\n", ans); return 0; } Day1 T2 货币系统 #include <bits/stdc++.h> using namespace std; int t, n, f[25005], a[105], ma, ans; void work() { memset(f, ma = 0, sizeof(f)); scanf("%d", &n); ans = n, f[0] = 1; for (int i = 1; i <= n; i++) scanf("%d", &a[i]), ma = max(a[i], ma); for (int i = 1; i

【 lca 】最近公共祖先

时光总嘲笑我的痴心妄想 提交于 2019-11-27 14:56:31
Step1 Problem: [原题] 给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。 Step2 Ideas: lca模板题,主要为了存模板。LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先 Step3 Code: Tarjan,离线算法,时间复杂度O(n + q) 1 // luogu-judger-enable-o2 2 #define _CRT_SECURE_NO_WARNINGS 3 #include<cstdio> 4 #include<iostream> 5 #include<cstring> 6 #include<algorithm> 7 #include<bitset> 8 #include<cassert> 9 #include<cctype> 10 #include<math.h> 11 #include<cstdlib> 12 #include<ctime> 13 #include<deque> 14 #include<iomanip> 15 #include<list> 16 #include<map> 17 #include<queue> 18 #include<set> 19 #include<stack> 20 #include<vector> 21 #define

天天爱跑步(NOIP2016)

匆匆过客 提交于 2019-11-27 13:48:26
传送门 终于把这个坑填了! 思路还是很值得学习的, 这篇博客 写的很详细。 重要的是拆成两种路径,推出这两个公式: up:T[i]+dep[i]=dep[s]; down:dep[v]-dis(u,v)=dep[i]-T[i] 两次dfs分别对应up和down两种情况,dfs里对于每一个i,统计其子树内满足等式的点s的个数(用捅保存),这样就对ans[i]做出贡献。 每次x处理完后要 删去桶中以i为LCA的路径的起点深度桶的值。 因为当我们遍历完i节点的孩子时,对于以i节点为LCA的路径来说。 这条路径上的信息对i的祖先节点是不会有影响的。(因为lca就是其能走到的最高的点了) 所以要将其删去。 注意: (摘自洛谷题解) 还有就是down里面下标可能为负,全部加一个N即可。 #include<bits/stdc++.h> #define N 300002 using namespace std; int read() { int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } struct EDGE{ int nextt,to; }w[N*2];

LCA(使用树链剖分)

好久不见. 提交于 2019-11-27 13:39:34
最近刚好学了树链剖分,来一道LCA的模板题练练手: P3379 最近公共祖先(LCA) 题意:给一棵多叉树,m次询问,求两个节点之间的LCA。 思路:树链剖分。 #include <bits/stdc++.h> #include<cstring> #define L(x) (x<<1) #define R(x) (x<<1|1) #define fori(a,b,c) for(int a=b;a<=c;a++) #define ford(a,b,c) for(int a=c;a>=b;a--) #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) using namespace std; const int M=5e5+100; vector<int> g[M]; int son[M],top[M],f[M],sz[M],dep[M],tot; void dfs1(int s,int fa) { f[s]=fa; dep[s]=dep[fa]+1; sz[s]=1; int ma=-1; for(int i=0;i<g[s].size();i++) { int v=g[s][i]; if(v==fa)continue; dfs1(v,s); sz[s]+=sz[v]; if(sz[v

树上差分略解

隐身守侯 提交于 2019-11-27 11:05:22
树上差分一般用于和树上路径有关的统计,对于一条路径 \((S,T)\) ,我们一般修改他们的 \(d[S],d[T],d[LCA]\) 的值来达到目的,而答案通常通过统计子树和来完成,复杂度显然为 \(O(n)\) 。 例一: 最大流 这题是一道树上差分的板子题。 显然用差分给对 \(d[S],d[T]\) 加一, \(d[LCA],d[las[LCA][0]]\) 减一,然后求子树和就相当于给树上路径节点加一。 不多 \(BB\) ,直接放代码。 // luogu-judger-enable-o2 #include<bits/stdc++.h> using namespace std; inline int read() { int f=1,w=0;char x=0; while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();} while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();} return w*f; } const int N=50010; int n,m,num_edge; int head[N<<1],d[N],las[N][21],Dep[N]; struct Edge{int next,to;} edge[N<<1]; inline void