树链剖分 [模板]最近公共祖先LCA

本人水平有限,题解不到为处,请多多谅解

本蒟蒻谢谢大家观看

题目:传送门

树链剖分:跑两遍dfs,第一遍找重边,第二遍找重链。

重儿子:父亲节点的所有儿子中子树结点数目最多(size最大)的结点;

轻儿子:父亲节点中除了重儿子以外的儿子;

重边:父亲结点和重儿子连成的边;

轻边:父亲节点和轻儿子连成的边;

重链:由多条重边连接而成的路径;

轻链:由多条轻边连接而成的路径

son[]表示重儿子,top[]表示重链所在的第一个节点,sz[]表示子节点数,fa[]表示父亲节点

图示:

code:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#pragma GCC optimize(3)

using namespace std;
int n,q,tot,s;
int head[1000010],nxt[10000010],ver[10000010],son[10000010],sz[10000010];
int top[10000010],dep[10000010],fa[10000010];
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
inline void write(int x){
     char F[200];
     int tmp=x>0?x:-x ;
     if(x<0)putchar(‘-‘) ;
     int cnt=0 ;
        while(tmp>0)
        {
            F[cnt++]=tmp%10+‘0‘;
            tmp/=10;
        }
        while(cnt>0)putchar(F[--cnt]) ;
}
void add(int x,int y){//字符链
    ++tot;
    ver[tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void dfs1(int x){
    sz[x]=1;//自己算一个节点
    son[x]=0;//自己的重儿子初始为0
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y!=fa[x]){
            fa[y]=x;//上 : y 的父亲节点为 x
            dep[y]=dep[x]+1;//中 : y的深度比x的深度多一
            dfs1(y);//先遍历子树
            sz[x]+=sz[y];//  下 :x的总结点数==字节点总数之和
            if(sz[son[x]]<sz[y])
                son[x]=y; //不断更新重儿子
        }
    }
    return ;
}
void dfs2(int x,int tp){//tp为x这条链的初始节点
    top[x]=tp;//x的初始节点为tp
    if(son[x]!=0)//若有重儿子
        dfs2(son[x],tp);//遍历重儿子
        //注意:此时重儿子的初始节点也为tp
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y!=son[x]&&y!=fa[x])//如果y既不在重儿子中,也不可能为父亲节点
            dfs2(y,y);//遍历y,因为son[x]已经遍历过了
    }
}
int query(int u,int v){//查找u,v的LCA
    while(top[u]!=top[v]){//如果u,v不在一条链上
        if(dep[top[u]]<dep[top[v]])//如果u的深度浅的话,要交换
            swap(u,v); //因为有可能向上跳的过程越过了LCA,保证深度必须超过其LCA
        u=fa[top[u]];//向上跳
    }
    if(top[u]==top[v]){//如果在一条链上
        if(dep[u]<dep[v])//输出深度浅的,因为深度越浅代表在上面,为u,v的LCA
            return u;
        else
            return v;
    }
}
int main()
{
    n=read(),q=read(),s=read();
    for(int i=1;i<n;i++){
        int x,y;
        x=read(),y=read();
        add(x,y),add(y,x);
    }
    dfs1(s);//以s为根
    dfs2(s,s);//以s为根,s为初始节点
    for(int i=1;i<=q;i++){
        int a,b;
        a=read(),b=read();
        printf("%d\n",query(a,b));
    }
    return 0;
}

树链剖分一定程度上类似于倍增,都是先确定大概范围,在具体寻找

原文地址:https://www.cnblogs.com/nlyzl/p/11624304.html

时间: 2024-07-30 11:04:20

树链剖分 [模板]最近公共祖先LCA的相关文章

树链剖分[模板](洛谷 P3384)

洛谷·[模板]树链剖分 写在前面 首先,在学树链剖分之前最好先把 LCA.树形DP.DFS序 这三个知识点学了 如果这三个知识点没掌握好的话,树链剖分难以理解也是当然的. 树链剖分 树链剖分 就是对一棵树分成几条链,把树形变为线性,减少处理难度 概念 dfs1() dfs2() 对剖过后的树建线段树 处理问题 概念 重儿子:对于每一个非叶子节点,它的儿子中 儿子数量最多的那一个儿子 为该节点的重儿子 轻儿子:对于每一个非叶子节点,它的儿子中 非重儿子 的剩下所有儿子即为轻儿子 叶子节点没有重儿子

树链剖分 模板

关于树链剖分 模板: 1 const int MAXN = 20010; 2 struct Edge 3 { 4 int to,next; 5 } edge[MAXN*2]; 6 int head[MAXN],tot; 7 8 int top[MAXN];//top[v]表示v所在的重链的顶端节点 9 int fa[MAXN]; //父亲节点 10 int deep[MAXN];//深度 11 int num[MAXN];//num[v]表示以v为根的子树的节点数 12 int p[MAXN];

HDU 3966 Aragorn&#39;s Story(树链剖分 模板题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966 Problem Description Our protagonist is the handsome human prince Aragorn comes from The Lord of the Rings. One day Aragorn finds a lot of enemies who want to invade his kingdom. As Aragorn knows, th

bzoj 1036 [ZJOI2008]树的统计Count 树链剖分模板

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 18615  Solved: 7598[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I

BZOJ 2243 染色 | 树链剖分模板题进阶版

BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上还是原树上,把两个区间的信息合并的时候,要注意中间相邻两个颜色是否相同. 这代码好长啊啊啊啊 幸好一次过了不然我估计永远也De不出来 #include <cstdio> #include <cstring> #include <algorithm> using namesp

树链剖分模板

1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cmath> 5 #include <algorithm> 6 #include <cstring> 7 #include <stack> 8 #include <cctype> 9 #include <queue> 10 #include <s

【树链剖分模板】【SPOJ 375】 Query on a tree

375. Query on a tree Problem code: QTREE You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of

树链剖分模板+入门题 SPOJ - QTREE

题目链接:[点击进入](http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=13013) 树链剖分并不是一个复杂的算法或者数据结构,只是能把一棵树拆成链来处理而已,换一种说法,树链剖分只是xxx数据结构/算法在树上的推广,或者说,树链剖分只是把树hash到了几段连续的区间上.比如说下面这道题,就是将树分为重链和轻链然后映射到线段树上,然后再在线段树上进行查询和修改等操作.所以树链剖分的重点有两个,一是正确的将树分解成几段并映射到

bzoj1036 [ZJOI2008]树的统计Count 树链剖分模板题

[ZJOI2008]树的统计Count Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成 一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 Input 输入的第一行为一个整数n,表示节点的个数.接下来n – 1行,每行