洛谷P3379 【模板】最近公共祖先(LCA)(树链剖分)

题目描述

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入输出格式

输入格式:

第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。

接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。

输出格式:

输出包含M行,每行包含一个正整数,依次为每一个询问的结果。

输入输出样例

输入样例#1: 复制

5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5

输出样例#1: 复制

4
4
1
4
4

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=10,M<=10

对于70%的数据:N<=10000,M<=10000

对于100%的数据:N<=500000,M<=500000

样例说明:

该树结构如下:

第一次询问:2、4的最近公共祖先,故为4。

第二次询问:3、2的最近公共祖先,故为4。

第三次询问:3、5的最近公共祖先,故为1。

第四次询问:1、2的最近公共祖先,故为4。

第五次询问:4、5的最近公共祖先,故为4。

故输出依次为4、4、1、4、4。

树链剖分求LCA

不断跳,跳到一条重链

输出在上面的

不用写线段树

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=2*1e6+10;
#define ls k<<1
#define rs k<<1|1
inline char nc()
{
    static char buf[MAXN],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXN,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    char c=nc();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=nc();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘,c=nc();}
    return x*f;
}
struct node
{
    int u,v,nxt;
}edge[MAXN];
int head[MAXN];
int num=1;
void AddEdge(int x,int y)
{
    edge[num].u=x;
    edge[num].v=y;
    edge[num].nxt=head[x];
    head[x]=num++;
}
int fa[MAXN],deep[MAXN],tot[MAXN],son[MAXN],top[MAXN],cnt;
int dfs1(int now,int f,int dep)
{
    deep[now]=dep;
    fa[now]=f;
    tot[now]=1;
    int maxson=-1;
    for(int i=head[now];i!=-1;i=edge[i].nxt)
    {
        if(edge[i].v==f) continue;
        tot[now]+=dfs1(edge[i].v,now,dep+1);
        if(tot[edge[i].v]>maxson) maxson=tot[edge[i].v],son[now]=edge[i].v;
    }
    return tot[now];
}
void dfs2(int now,int topf)
{
    top[now]=topf;
    if(!son[now]) return ;
    dfs2(son[now],topf);
    for(int i=head[now];i!=-1;i=edge[i].nxt)
        if(edge[i].v!=son[now]&&edge[i].v!=fa[now])
            dfs2(edge[i].v,edge[i].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()
{
    #ifdef WIN32
    freopen("a.in","r",stdin);
    #else
    #endif
    memset(head,-1,sizeof(head));
    int N=read(),M=read(),root=read();
    for(int i=1;i<=N-1;i++)
    {
        int x=read(),y=read();
        AddEdge(x,y);AddEdge(y,x);
    }
    dfs1(root,0,1);
    dfs2(root,root);
    while(M--)
    {
        int x=read(),y=read();
        printf("%d\n",LCA(x,y));
    }
    return 0;
}
时间: 2024-10-01 00:31:56

洛谷P3379 【模板】最近公共祖先(LCA)(树链剖分)的相关文章

洛谷 P2146 [NOI2015]软件包管理器 (树链剖分模板题)

题目描述 Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软件包的安装所依赖的其它软件包),完成所有的配置.Debian/Ubuntu使用的apt-get,Fedora/CentOS使用的yum,以及OSX下可用的homebrew都是优秀的软件包管理器. 你决定设计你自己的软件包管理器.不可避免地,你要解决软件包之间的依赖问题.如果软件包A依赖软件包B,那

洛谷P3128 [USACO15DEC]最大流Max Flow [树链剖分]

题目描述 Farmer John has installed a new system of  pipes to transport milk between the  stalls in his barn (), conveniently numbered . Each pipe connects a pair of stalls, and all stalls are connected to each-other via paths of pipes. FJ is pumping milk

Count on a tree SPOJ 主席树+LCA(树链剖分实现)(两种存图方式)

Count on a tree SPOJ 主席树+LCA(树链剖分实现)(两种存图方式) 题外话,这是我第40篇随笔,纪念一下.<( ̄︶ ̄)[GO!] 题意 是说有棵树,每个节点上都有一个值,然后让你求从一个节点到另一个节点的最短路上第k小的值是多少. 解题思路 看到这个题一想以为是树链剖分+主席树,后来写着写着发现不对,因为树链剖分我们分成了一小段一小段,这些小段不能合并起来求第k小,所以这个想法不对.奈何不会做,查了查题解,需要用LCA(最近公共祖先),然后根据主席树具有区间加减的性质,我们

LCA 树链剖分

//LCA //树链剖分 在线 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; int n,m,root,cnt,head[500001]; int hvyson[500001],fa[500001],siz[500001],d

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

本人水平有限,题解不到为处,请多多谅解 本蒟蒻谢谢大家观看 题目:传送门 树链剖分:跑两遍dfs,第一遍找重边,第二遍找重链. 重儿子:父亲节点的所有儿子中子树结点数目最多(size最大)的结点: 轻儿子:父亲节点中除了重儿子以外的儿子: 重边:父亲结点和重儿子连成的边: 轻边:父亲节点和轻儿子连成的边: 重链:由多条重边连接而成的路径: 轻链:由多条轻边连接而成的路径 son[]表示重儿子,top[]表示重链所在的第一个节点,sz[]表示子节点数,fa[]表示父亲节点 图示: code: #i

BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

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

HDOJ 5293 Tree chain problem LCA+树链剖分+树形DP

[题意] 给定一颗树上的几条链和每条链的权值,求能取出的不含有公共节点的链的最大权值.... [解] 预处理每条链的lca 树形DP, d[i]表示取到这个节点时可以得到的最大值 , sum[i]=sigma( d[k] | k 是i的子节点) 如果不取i  d[i]=sum[i] 如果取i , e是lca为i的链则 d[i]=max(d[i],e的权值+sigma(sum[k])-sigma(d[k]))  k为树链上的点 可以用树链剖分+树装数组在nlogn的时间复杂度内求链上的值 Tree

BZOJ 3626 LNOI 2014 LCA 树链剖分

题目大意:给出一棵树,有n个问题,询问在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和. 思路:不会,然后看了题解,之后发现自己智商严重不足. 看到数据范围就知道一定要离线处理,就这个离线处理我估计以我的智商不看题解是肯定想不出来的.. 考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案.观察到,深度其实就是上面有几个已标记了的点(包括自身).所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r

BZOJ3626 LNOI2014 LCA 树链剖分

题意:给定一棵树,每次询问给出l r z,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和 题解: 显然,暴力求解的复杂度是无法承受的. 考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案.观察到,深度其实就是上面有几个已标记了的点(包括自身).所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和.仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆

[模板]最近公共祖先LCA

本人水平有限,题解不到为处,请多多谅解 本蒟蒻谢谢大家观看 题目:传送门 倍增求LCA模板 code: #include<bits/stdc++.h> #pragma GCC optimize(3) using namespace std; int n,q,a,b,tot,m; int nxt[1000010],head[1000010],ver[1000010],dep[1000010],f[1000010][21]; //设f[x,k]表示x的2^k辈祖先,即从x向根节点走2^k步到达的节