题目描述

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

输入输出格式

输入格式:

第一行包含三个正整数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。

题解

emmmmmm

这道题听说倍增写得丑了就过不了。。。

所以我正好拿最近刚学的树链剖分练练手

虽然看到题解里有树剖。。。

但我自认为自己写的比较优美。。。

而且最关键的是题解里好多都在说树剖慢。。。

他们怕不是写了假的树剖。。。

下面给出严格【并不】的复杂度证明:

树剖要用到两次dfs,都是O(n)的复杂度

然后如果图是满二叉树的话是最坏情况,但此时查询也是O(logn)的

【显然最坏情况每次一步步取f[top[x]]走,走下来是一个树的深度,也就是一个logn】

所以理论复杂度上界就是O(2n+mlogn)

而且关键的是树剖常数还比倍增要小。。。

倍增的话常数虽然也很小

但是某集训队dalao写过一篇论文里面有严格的树剖的常数证明,计算结果是树剖的常数接近1/2

再加上树剖用的的空间也比倍增啊RMQ啊小

所以写树剖是完全没问题滴~

那么下面就简单说下树剖思路咯

树剖就是把树剖分成若干条不相交的链,目前常用做法是剖成轻重链

所以我们定义siz[x]为以x为根结点的子树的结点个数

对于每个结点x,在它的所有子结点中寻找一个结点y

使得对于y的兄弟节点z,都有siz[y]≥siz[z]

此时x就有一条重边连向y,有若干条轻边连向他的其他子结点【比如z】

这样的话,树上的不在重链上的边的数量就会大大减少

然后我们每次求LCA(x,y)的时候就可以判断两点是否在同一链上

如果两点在同一条链上我们只要找到这两点中深度较小的点输出就行了

如果两点不在同一条链上

那就找到深度较大的点令它等于它所在的重链链端的父节点即为x=f[top[x]]

直到两点到达同一条链上,输出两点中深度较小的点

代码如下

//by 减维
#include<cstdio>
#include<iostream>
using namespace std;
struct edge{
int to,ne;
}e[];
int n,m,s,ecnt,head[],dep[],siz[],son[],top[],f[];
void add(int x,int y)
{
e[++ecnt].to=y;
e[ecnt].ne=head[x];
head[x]=ecnt;
}
void dfs1(int x)
{
siz[x]=;
dep[x]=dep[f[x]]+;
for(int i=head[x];i;i=e[i].ne)
{
int dd=e[i].to;
if(dd==f[x])continue;
f[dd]=x;
dfs1(dd);
siz[x]+=siz[dd];
if(!son[x]||siz[son[x]]<siz[dd])
son[x]=dd;
}
}
void dfs2(int x,int tv)
{
top[x]=tv;
if(son[x])dfs2(son[x],tv);
for(int i=head[x];i;i=e[i].ne)
{
int dd=e[i].to;
if(dd==f[x]||dd==son[x])continue;
dfs2(dd,dd);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(int i=;i<n;++i)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs1(s);
dfs2(s,s);
for(int i=;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
while(top[x]!=top[y])
{
if(dep[top[x]]>=dep[top[y]])x=f[top[x]];
else y=f[top[y]];
}
printf("%d\n",dep[x]<dep[y]?x:y);
}
}

【树链剖分】洛谷P3379 树链剖分求LCA的更多相关文章

  1. 洛谷 3379 最近公共祖先(LCA 倍增)

    洛谷 3379 最近公共祖先(LCA 倍增) 题意分析 裸的板子题,但是注意这题n上限50w,我用的边表,所以要开到100w才能过,一开始re了两发,发现这个问题了. 代码总览 #include &l ...

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

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  3. 【算法学习】【洛谷】树链剖分 & P3384 【模板】树链剖分 P2146 软件包管理器

    刚学的好玩算法,AC2题,非常开心. 其实很早就有教过,以前以为很难就没有学,现在发现其实很简单也很有用. 更重要的是我很好调试,两题都是几乎一遍过的. 介绍树链剖分前,先确保已经学会以下基本技巧: ...

  4. 洛谷P3384 树链剖分

    如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x ...

  5. 洛谷P2146 树链剖分

    题意 思路:直接树链剖分,用线段树维护即可,算是树剖的经典题目吧. 代码: #include <bits/stdc++.h> #define ls(x) (x << 1) #d ...

  6. 洛谷 P3379 【模板】最近公共祖先(LCA)

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  7. 【倍增】洛谷P3379 倍增求LCA

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  8. 【RMQ】洛谷P3379 RMQ求LCA

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  9. 【Tarjan】洛谷P3379 Tarjan求LCA

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

随机推荐

  1. 用KMP算法实现strStr()

    strStr()函数的用途是在一个字符串S中寻找某个字串P第一次出现的位置.并返回其下标,找不到时返回-1.最简单的办法就是找出S全部的子串和P进行比較,然而这种方法比較低效.假设我们从S的下标0和P ...

  2. PAT 1004 To Fill or Not to Fill (25)

    题目描写叙述 With highways available, driving a car from Hangzhou to any other city is easy. But since the ...

  3. Codeforces Round #271 (Div. 2) F题 Ant colony(线段树)

    题目地址:http://codeforces.com/contest/474/problem/F 由题意可知,最后能够留下来的一定是区间最小gcd. 那就转化成了该区间内与区间最小gcd数相等的个数. ...

  4. Liunx的常用命令

    常用指令 ls 显示文件或目录 -l 列出文件详细信息l(list) -a 列出当前目录下所有文件及目录,包括隐藏的a(all) mkdir 创建目录 -p 创建目录,若无父目录,则创建p(paren ...

  5. 配置Meld为git的默认比较工具

    1. 安装 meld sudo apt-get install meld 2. 创建 git_meld.sh 脚本 cd /bin vim git-meld.sh #!/bin/sh meld $2 ...

  6. MQTT Client library for C (MQTT客户端C语言库-paho)

    原文:http://www.eclipse.org/paho/files/mqttdoc/MQTTClient/html/index.html 来自我的CSDN博客   最近在使用Paho的MQTT客 ...

  7. IDEA 初始配置教程

    IDEA 初始配置教程 如果你是第一次使用 IDEA,或者对 IDEA 常用配置仍然不熟悉,那么本文就特别适合你. 本文只是根据我自己的使用经验来进行配置,不一定适合所有的情况,但是对你肯定会有帮助. ...

  8. Nginx优点

    1.高并发响应性能非常好,官方Nginx处理静态文件并发5w/s 2.反向代码性能非常强(可用于负载均衡) 3.内存和cpu占比率低(为Apache的1/5-1/10); 4.对后端服务有健康检查功能 ...

  9. spring boot利用swagger和spring doc生成在线和离线文档

    参考博客地址: 在线文档:http://blog.didispace.com/springbootswagger2/ 离线文档:http://www.jianshu.com/p/af7a6f29bf4 ...

  10. CentOS 7部署ASP.NET Core应用程序

    看了几篇大牛写的关于Linux部署ASP.NET Core程序的文章,今天来实战演练一下.2017年最后一个工作日,提前预祝大家伙元旦快乐.不扯淡,直接进入正题.您有任何问题请在评论区留言. 1.环境 ...