LCA 倍增||树链剖分

方法1:倍增

1498ms

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=5e5+5;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}
int n,q,root,a,b;
struct edge{
    int v,ne;
}e[N<<1];
int cnt=0,h[N];
inline void ins(int u,int v){
    cnt++;
    e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
    cnt++;
    e[cnt].v=u;e[cnt].ne=h[v];h[v]=cnt;
}
int fa[N][21],deep[N],vis[N];
void dfs(int u){
    vis[u]=1;
    for(int j=1;(1<<j)<=deep[u];j++)
        fa[u][j]=fa[fa[u][j-1]][j-1];
    for(int i=h[u];i;i=e[i].ne){
        int v=e[i].v;
        if(vis[v]) continue;
        deep[v]=deep[u]+1;
        fa[v][0]=u;
        dfs(v);
    }
}
int lca(int x,int y){
    if(deep[x]<deep[y]) swap(x,y);
    int bin=deep[x]-deep[y];
    for(int i=0;i<=16;i++)
        if((1<<i)&bin) x=fa[x][i];//,printf("x %d\n",i);

    for(int i=16;i>=0;i--)
        if(fa[x][i]!=fa[y][i]){
            x=fa[x][i];
            y=fa[y][i];
        }
    if(x==y) return x;
    else return fa[x][0];
}
int main(int argc, const char * argv[]) {
    n=read();q=read();root=read();
    for(int i=1;i<=n-1;i++) a=read(),b=read(),ins(a,b);
    dfs(root);
    for(int i=1;i<=q;i++){
        a=read();b=read();
        printf("%d\n",lca(a,b));
    }
    return 0;
}

方法2:树链剖分

1314ms

让链首深度大的走到重链的父节点直到在一条重链上,返回深度小的节点

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=5e5+5;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}
int n,q,root,a,b;
struct edge{
    int v,ne;
}e[N<<1];
int cnt=0,h[N];
inline void ins(int u,int v){
    cnt++;
    e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
    cnt++;
    e[cnt].v=u;e[cnt].ne=h[v];h[v]=cnt;
}
int fa[N],deep[N],mx[N],size[N];
void dfs(int u){
    size[u]++;
    for(int i=h[u];i;i=e[i].ne){
        int v=e[i].v;
        if(v==fa[u]) continue;
        fa[v]=u;deep[v]=deep[u]+1;
        dfs(v);
        size[u]+=size[v];
        if(size[v]>size[mx[u]]) mx[u]=v;
    }
}
int tid[N],top[N],tot;
void dfs(int u,int anc){
    if(!u) return;
    tid[u]=++tot;top[u]=anc;
    dfs(mx[u],anc);
    for(int i=h[u];i;i=e[i].ne){
        int v=e[i].v;
        if(v!=fa[u]&&v!=mx[u]) dfs(v,v);
    }
}
int lca(int x,int y){
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    if(deep[x]>deep[y]) swap(x,y);
    return x;
}
int main(int argc, const char * argv[]) {
    n=read();q=read();root=read();
    for(int i=1;i<=n-1;i++) a=read(),b=read(),ins(a,b);
    dfs(root);
    dfs(root,root);
    for(int i=1;i<=q;i++){
        a=read();b=read();
        printf("%d\n",lca(a,b));
    }
    return 0;
}
时间: 2024-10-03 20:16:02

LCA 倍增||树链剖分的相关文章

洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

洛谷题目传送门 %%%天平巨佬和山楠巨佬%%% 他们的题解 思路分析 具体思路都在两位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 首先kruskal把最小生成树弄出来,因为要求次小生成树.至于为什么次小一定只在最小的基础上改变了一条边,我也不会证......打表找规律大法好 剩下的可以有一堆数据结构来维护最大值和次大值(原理两位巨佬都讲清楚了,这里只分析一下算法的优劣) 倍增+LCA 山楠巨佬的做法,我也写了这一种.复杂度\(O(MlogM(kruscal)+MlogN(

从lca到树链剖分 bestcoder round#45 1003

bestcoder round#45 1003 题,给定两个点,要我们求这两个点的树上路径所经过的点的权值是否出现过奇数次.如果是一般人,那么就是用lca求树上路径,然后判断是否出现过奇数次(用异或),高手就不这么做了,直接树链剖分.为什么不能用lca,因为如果有树退化成链,那么每次询问的复杂度是O(n), 那么q次询问的时间复杂度是O(qn) 什么是树链剖分呢? 就是把树的边分成轻链和重链 http://blogsina.com.cn/s/blog_6974c8b20100zc61.htmlh

BZOJ 3626 LCA(离线+树链剖分)

首先注意到这样一个事实. 树上两个点(u,v)的LCA的深度,可以转化为先将u到根路径点权都加1,然后求v到根路径上的总点权值. 并且该题支持离线.那么我们可以把一个区间询问拆成两个前缀和形式的询问. 现在问题就变成了求[1,r]和x的LCA深度之和.实际上就是把[1,r]到根路径点权点1,然后求x到根路径上的总权值. 我们按编号从小往大依次加路径点权.然后就可以有序处理询问.用树链剖分维护的话,总复杂度为O((n+q)lognlogn).

CF 191C Fools and Roads lca 或者 树链剖分

They say that Berland has exactly two problems, fools and roads. Besides, Berland has n cities, populated by the fools and connected by the roads. All Berland roads are bidirectional. As there are many fools in Berland, between each pair of cities th

bzoj 3626: [LNOI2014]LCA 离线+树链剖分

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 426  Solved: 124[Submit][Status] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)].(即

[BZOJ 3626] [LNOI2014] LCA 【树链剖分 + 离线 + 差分询问】

题目链接: BZOJ - 3626 题目分析 考虑这样的等价问题,如果我们把一个点 x 到 Root 的路径上每个点的权值赋为 1 ,其余点的权值为 0,那么从 LCA(x, y) 的 Depth 就是从 y 到 Root 的路径上的点权和. 这个方法是可以叠加的,这是非常有用的一点.如果我们把 [l, r] 的每个点到 Root 的路径上所有点的权值 +1,再求出从 c 到 Root 的路径点权和,即为 [l, r] 中所有点与 c 的 LCA 的 Depth 和. 不仅满足可加性,还满足可减

【BZOJ3626】[LNOI2014]LCA 离线+树链剖分+线段树

[BZOJ3626][LNOI2014]LCA Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)].(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和) Input 第一行2个整数n q.接下来n-1行,分别表示点1到点n-1的父节点编号.接

bzoj3626: [LNOI2014]LCA (树链剖分)

很神奇的方法 感觉是有生之年都想不到正解的这种 考虑对i 到根的节点权值 + 1,则从根到z的路径和就是lca(i,z)的深度 所以依次把0 ~ n - 1的点权值 + 1 对于询问[l, r] 这个区间关于z 的深度和,就用(1, r) - (1, l - 1)的值表示 详见黄学长的博客啦 http://hzwer.com/3415.html 下面给出代码 #include <cstdio> #include <vector> #include <algorithm>

bzoj3626: [LNOI2014]LCA 离线+树链剖分

理解了半天. 题解:http://hzwer.com/3891.html #include<bits/stdc++.h> #define N 50010 #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define K l,r,k #define L l,M,P #define R M+1,r,S #define Z int l=1,int r=n,int k=1 using namespace