lca

NOIP2016提高组 天天爱跑步

 ̄綄美尐妖づ 提交于 2019-11-28 11:22:19
(树上差分 \(+\) \(LCA\) ) \(O(Mlog_2N)\) 调了两个小时,最后发现把 \(lca\) 里的 \(y\) 写成 \(x\) 了,当场去世。 首先下几个定义: \(dis[x]\) 为 \(x\) 到根节点的距离。由于边权都是 \(1\) ,所以 \(dis[x] = dep[x]\) \(LCA(x, y)\) 为 \(x, y\) 的最近公共祖先 \(LCA(x, y)\ down\) 为 \(x, y\) 的最近公共祖先在往 \(y\) 的放下下去一格(这里不懂可以看下面的图) \(ans[x]\) 为这个点的答案 我们称玩家跑的路线为"一条路径" 对于第 \(i\) 个玩家,发现可以将从 \(S_i\) 到 \(T_i\) 的路径分成两条链: \(S_i\) 到 \(LCA(S_i, T_i)\) \(LCA(S_i, T_i)\ down\) 到 \(T_i\) (这里不算 \(LCA\) ,不然会重复两次 ) 不太理解的同学看这张图,设 \(S_i = 6, T_i = 8\) 。其中$ LCA(6, 8) down = 5$ 然后我们进行分类讨论: 从起点出发,终点在第一条链上(上升链): 考虑一个玩家 \(i\) 对答案的贡献: 这条玩家的起点为 \(S_i\) ,终点为 \(LCA(S_i, T_i)\) 。

AtCoder Beginner Contest 133 F - Colorful Tree

让人想犯罪 __ 提交于 2019-11-28 08:37:45
题意:给出一棵n个点的树,每条边有颜色和边长两个属性,n个询问,每次询问把颜色x的边的边长变为y问u到v的路径长度是多少,询问之间独立。 解法:这道题蛮有意思的。解法参考 https://www.cnblogs.com/Tieechal/p/11185912.html 这位大佬的,这里说下我的理解。 对于每组询问(x,y,u,v)答案比较显然就是dist(u,v)+(sumlen[x]-sumcnt[x]*y),但是这道题在线不好做我们考虑离线做。但是答案的式子是设计到两个点的,怎么才能离线做呢?这里运用了一种比较巧妙地办法:利用LCA把询问拆成3个点,然后直接一遍dfs离线即可。具体来说就是把每个询问拆成u,v,lca三个点,这三个点对询问造成地贡献就是答案,每个点p的贡献就是:根到p点的路径+根到p点路径中颜色x的路径总和-根到p点颜色x的边数*y(当然三个点的系数是不一样的)。根据LCA求两点距离的经验不难看出其实这里的原理差不多,也是分别算跟到两点然后除去到lca的重复部分就是正确的。 那么先dfs一遍求LCA,然后再离线dfs一遍处理询问就可以了。 细节详见代码: #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,m,t; typedef long long LL; struct

树链剖分求lca

柔情痞子 提交于 2019-11-28 06:10:52
void dfs1(int u,int f) { fa[u]=f,siz[u]=1,dep[u]=dep[f]+1; int maxson = -1; for(int i=Head[u];~i;i=Edge[i].next) { int &v = Edge[i].to; if(v==f) continue; dfs1(v,u); siz[u]+=siz[v]; if(siz[v]>maxson) maxson=siz[v],son[u]=v; } } int top[maxn]; void dfs2(int u,int t) { top[u]=t; if(!son[u]) return; dfs2(son[u],t); for(int i=Head[u];~i;i=Edge[i].next) { int &v = Edge[i].to; if(v==fa[u]||v==son[u]) continue; dfs2(v,v); } } int main() { memset(Head,-1,sizeof(Head)); int n,m,s; read(n,m,s); while(--n) { int u,v; read(u,v); AddEdge(u,v); } dfs1(s,s); dfs2(s,s); while(m--) { int u,v; read(u,v); while

LCA题解

我与影子孤独终老i 提交于 2019-11-28 06:06:00
LCA题解 神奇化简题,谁看得出这是树剖。 首先, \(lca\) 的祖宗对 \(lca\) 都有贡献,我们不妨将 \(0\) 到 \(lca\) 路径的权值都加 \(1\) ,则 \(lca\) 的 \(deep\) 就为 \(0\) 到 \(lca\) 的路径权值和 则 \(\sum_{i=l}^{r}deep[lca_{i}]=\sum_{i=l}^{r}\sum_{j=lca_{i}}^{j\epsilon path(0,lca_{i})}w[j]\) 我们想想在不会 \(lca\) 的时候如何暴力搞两个点 \(x\) 和 \(y\) 的l \(ca\) ? 我们可以明确深度大于 \(lca\) 的点对于 \(lca\) 不会有贡献,且 \(deep[x]>=deep[lca]\) 不妨将我们不妨将 \(0\) 到 \(x\) 路径的权值都加 \(1\) , \(deep[lca]=\sum_{i=y}^{i\epsilon path(0,y)}w[i]\) 感性理解一下吧 如此我们需要枚举 \(i\) 从 \(l\) 到 \(r\) ,将 \(0\) 到 \(i\) 路径的权值都加 \(1\) , \(\sum_{i=l}^{r}deep[lca(i,z)]=\sum_{i=z}^{i\epsilon path(0,z)}w[i]\) 这样一次查询时间复杂度为 \(O(n

【SDOI2013】森林

我与影子孤独终老i 提交于 2019-11-28 04:10:48
链接 先考虑对于一棵树进行路径查询,当然可以树链剖分,但没必要。 对于一个询问 [ x , y ] ,可以转化为 [ x , root ] + [ y , root ] - 2 [ lca , root ] + lca,即 [ x , root ] + [ y , root ] - [ lca , root ] - [ fa [ lca ] , root ] 所以只要统计节点到根节点的信息就可以了,求第k小,显然要用主席树。 我们知道,主席树的基本思想是前缀和,在序列上时,以前一个为pre;在树上时,只要以这个节点的父亲为pre就可以了。 这样就解决了询问。 对于连边,可以启发式合并。简而言之就是小树往大树上连。 启发式合并中,每个节点只有在当前size较小时才会被合并,每次合并后体积至少扩大为原来的两倍。所以总复杂度是O(NlogN)。因为更新倍增数组和插入是O(logN)的,所以带了两个log,还是可以接受的。 最后注意要离散化,数组要开大。 1 #include<cstdio> 2 #include<algorithm> 3 #include<string> 4 #include<cstring> 5 #include<cstdlib> 6 using namespace std; 7 8 int read(){ 9 int x=0;char ch=getchar(); 10

「CTSC2018」暴力写挂

≡放荡痞女 提交于 2019-11-28 04:02:26
题目链接 「CTSC2018」暴力写挂 做法 \[ dep(x) + dep(y) - dep(LCA(x, y)) - dep'(LCA'(x, y))\\\\ = \frac{1}{2} (dep(x) + dep(y) - 2dep(LCA(x, y)) + dep(x) + dep(y) - 2dep'(LCA'(x, y)))\\\\ = \frac{1}{2}(dis(x, y) + dep(x) + dep(y) - 2dep'(LCA'(x, y))) \] 考虑对第一棵树边分治。设当前分治重心为 $ U, V $ ,选择 $ U $ 侧节点 $ X $ ,选择 $ V $ 侧节点 $ Y $ ,则令 $ X $ 为一类节点,贡献为 $ e1(X) = dep(X) + dis(V, X) $ ;令 $ Y $ 为二类节点,贡献为 $ e2(Y) = dep(Y) + dis(V, Y) $ 。枚举第二颗树的 $ LCA $ ,设 $ X, Y $ 在第二颗树中的 $ LCA $ 为 $ lca $ ,则对答案的贡献为 $ \frac{1}{2}(e1(X) + e2(Y) - 2dep(lca)) $ 。由于需要正确的时间复杂度,所以需要对第二颗树建虚树进行 $ DP $ 。 不优秀的实现会导致时间复杂度为 $ O(n \log^2 n) $

LCA

旧时模样 提交于 2019-11-28 03:57:38
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 const int maxn = 1e6 + 5; 7 struct node 8 { 9 int u , v , next; 10 }G[maxn]; 11 int tail , head[maxn]; 12 int n , m , s; 13 int depth[maxn] , father[maxn][25] , lg[maxn]; 14 void add(int u , int v) 15 { 16 tail++; 17 G[tail].u = u , G[tail].v = v , G[tail].next = head[u]; 18 head[u] = tail; 19 } 20 void dfs(int p , int fa) 21 { 22 depth[p] = depth[fa] + 1; 23 father[p][0] = fa; 24 for(int i = 1; (1 << i) <= depth[p]; i++) 25 father[p][i] = father[father[p][i - 1]][i - 1]; 26 for(int i

ST表求LCA

萝らか妹 提交于 2019-11-28 03:46:08
https://www.luogu.org/problemnew/show/SP14932 思路:先遍历一遍得到欧拉序,然后根据询问找到对应节点在欧拉序第一次出现的位置.left, right,然后欧拉序中在left到right之间的深度最小的节点就是LCA。 如上图: 从4遍历得到的欧拉序是 4 2 4 1 3 1 5 1 4 得到的对应节点深度 1 2 1 2 3 2 3 2 1 比如查询3 和 2 的最近公共祖先,3 和 2 在欧拉序中第一次出现的位置 是 2 和 5 ,在欧拉序中他们之间的点(包括自己)有 2 4 1 3 ,深度最小是 1 ,即 节点4。所以3 和2 的LCA是4 #include<iostream> #include<cstdio> #include<vector> using namespace std; const int maxn = 5e5 + 5; const int maxbit = 20; vector<int>G[maxn]; int order[maxn * 4];//记录欧拉序 int depth[maxn * 4];//记录欧拉序序列每个点的深度 int n,m,s;//节点个数,询问次数,根节点 int lg[maxn * 4];//以2为底向下取整 int ST[maxn * 4][maxbit];/

P2633 Count on a tree

此生再无相见时 提交于 2019-11-28 01:16:28
题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。 输入格式 第一行两个整数N,M。 第二行有N个整数,其中第i个整数表示点i的权值。 后面N-1行每行两个整数(x,y),表示点x到点y有一条边。 最后M行每行两个整数(u,v,k),表示一组询问。 输出格式 M行,表示每个询问的答案。 输入输出样例 输入 #1 8 5 105 2 9 3 8 5 7 7 1 2 1 3 1 4 3 5 3 6 3 7 4 8 2 5 1 0 5 2 10 5 3 11 5 4 110 8 2 输出 #1 2 8 9 105 7 说明/提示 HINT: N,M<=100000 暴力自重。。。 来源:bzoj2588 Spoj10628. 本题数据为洛谷自造数据,使用 CYaRon 耗时5分钟完成数据制作。 主席树乱搞+lca #include<bits/stdc++.h> using namespace std; const int maxn = 100010; int n,m,root[maxn],size,head[maxn],lastans,f[maxn][20],dep[maxn],cnt,rank[maxn]; struct

洛谷p4211 [LNOI2014] LCA

折月煮酒 提交于 2019-11-28 00:18:15
[LNOI2014] LCA 题面 洛谷 题解 水题。 对于一个区间 \([l,r]\) 的答案。 我们把它差分成 \([1,r] - [1,l - 1]\) 然后因为右端点是固定的,用树剖+只有1个端点的莫队就解决了... 你谷恶评实在太严重了。 #include <bits/stdc++.h> const int maxn = 5e4 + 10; const int mod = 201314; template<class t> inline void read(t& res) { res = 0; char ch = getchar(); bool neg = 0; while (!isdigit(ch)) neg |= ch == '-', ch = getchar(); while (isdigit(ch)) res = (res << 1) + (res << 3) + (ch & 15), ch = getchar(); if (neg) res = -res; } int q, n, m, i, j, k, cntq; int hd[maxn], ver[maxn << 1], nxt[maxn << 1], ans[maxn]; struct Questions { int p, z, id; int type; Questions() { p = z =