HDU6031 Innumerable Ancestors 倍增 - 题意详细概括 - 算法详解
题目
查看原题 - HDU6031 Innumerable Ancestors
题目描述
有一棵有n个节点的有根树,根节点为1,其深度为1,现在有m个询问,每次询问给出两个集合A和B,问LCA(x,y)(x∈A,y∈B)的深度最大为多少。
输入描述
有多组数据(数据组数<=5)
对于每一组数据,首先2个数n,m,表示有根树的节点个数和询问个数。然后n-1行,每行2个数a,b表示节点a和节点b之间存在直接的连边;接下去2m行,每两行,分别描述当前询问的集合A和集合B;对于一个集合,用一行来描述,该行第一个数K表示集合元素的个数,后面K个数表示集合中的元素。
输出描述
一个整数,表示LCA(x,y)(x∈A,y∈B)的最大深度。
数据范围
n,m<=100000, 1<=a,b<=n, ΣK<=100000, 1<=集合中的元素<=n
题解
问最大深度,那么我们思考是否可以二分答案。
当然可以,本题的条件满足二分答案的前提,LCA基本的性质还是比较明显的。(假设a和b深度一样)设anst[x][y]为节点x往上走y步到达的祖先,对于一个k,如果anst[a][k]==anst[b][k],那么对于k'(k'>k),一定有anst[a][k']==anst[b][k'];对于一个k,如果anst[a][k]!=anst[b][k],那么对于k'(k'<k),一定有anst[a][k']!=anst[b][k'],而且LCA(a,b)=LCA(anst[a][k],anst[b][k])。
二分答案深度d完成之后,那么就剩下了编一个子程序判定的事情了。
那么如果判定呢?
已知祖先深度,那么就知道了每一个点所对应的祖先了是吧?那么,判断是否有公共祖先,其实就是判断A集合所对应的祖先集合与B集合所对应的祖先集合是否有交集——因为ΣK<=100000, 所以对于每一个集合元素找出它的某一深度的祖先这个复杂度貌似还是不够,ΣK*n应该会超(如果有人用ΣK*n的判定复杂度过了本题, 跪求留代码) 。那么我们要更快的找到这个祖先,那么是什么?倍增啊!
fa[i][j]表示与节点i深度差为2^j的i的祖先,那么不难写出转移方程:
fa[i][0]=father[i],fa[i][j]=fa[fa[i][j-1]][j-1] (father[i]表示节点i的父亲节点)
So,求某一深度的祖先就是和倍增求LCA的前一半类似的了。
至于两个集合判断交集,就是排个序,然后两个指针扫过去就可以了。
注意: 在求祖先时,要首先把那些不合法的祖先过滤掉; 在判断交集的时候,要注意边界情况!
代码
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=,M=N*,rt=;
struct Edge{
int cnt,y[M],nxt[M],fst[N];
void set(){
cnt=;
memset(y,,sizeof y);
memset(nxt,,sizeof nxt);
memset(fst,,sizeof fst);
}
void add(int a,int b){
y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
}
}e;
int n,m,depth[N],fa[N][],ta,a[N],tb,b[N],ansta[N],anstb[N];
void build(int prev,int rt){
fa[rt][]=prev,depth[rt]=depth[prev]+;
for (int i=;(<<i)<=depth[rt];i++)
fa[rt][i]=fa[fa[rt][i-]][i-];
for (int i=e.fst[rt];i;i=e.nxt[i])
if (e.y[i]!=prev)
build(rt,e.y[i]);
}
int get_kth_anst(int p,int k){
for (int i=k,j=;i>;i>>=,j++)
if (i&)
p=fa[p][j];
return p;
}
bool check(int d){
int at=,bt=;
for (int i=;i<=ta;i++)
if (depth[a[i]]>=d)
ansta[++at]=get_kth_anst(a[i],depth[a[i]]-d);
for (int i=;i<=tb;i++)
if (depth[b[i]]>=d)
anstb[++bt]=get_kth_anst(b[i],depth[b[i]]-d);
if (at==||bt==)
return ;
int pa=,pb=;
sort(ansta+,ansta+at+);
sort(anstb+,anstb+bt+);
if (ansta[]==anstb[])
return ;
while (pa<=at&&pb<=bt){
while (pa<=at&&ansta[pa]<anstb[pb])
pa++;
if (pa>at)
break;
if (ansta[pa]==anstb[pb])
return ;
while (pb<=bt&&ansta[pa]>anstb[pb])
pb++;
if (pb>bt)
break;
if (ansta[pa]==anstb[pb])
return ;
}
return ;
}
int main(){
while (~scanf("%d%d",&n,&m)){
e.set();
for (int i=,a,b;i<n;i++)
scanf("%d%d",&a,&b),e.add(a,b),e.add(b,a);
depth[]=-;
build(,rt);
while (m--){
scanf("%d",&ta);
for (int i=;i<=ta;i++)
scanf("%d",&a[i]);
scanf("%d",&tb);
for (int i=;i<=tb;i++)
scanf("%d",&b[i]);
int le=,ri=n-,mid,ans=;
while (le<=ri){
mid=(le+ri)>>;
if (check(mid))
le=mid+,ans=mid;
else
ri=mid-;
}
printf("%d\n",ans+);
}
}
return ;
}
为了方便大家找茬,特地附上一份造数据的PASCAL代码,用于对拍。
var
t, i: longint;
function min(a, b: longint): longint;
begin
if (a > b) then
exit(b);
exit(a);
end;
procedure make_list(n ,m: longint);
var
i, j: longint;
begin
write(m, ' ');
j := ;
for i := to m do
begin
j := j + random(n - j - m + i) + ;
write(j, ' ');
end;
writeln;
end;
procedure mkdata;
const
maxn = ;
maxm = ;
add = ;
var
n, m, i, j, x, y, a, b: longint;
begin
n := random(maxn) + ;
m := random(maxm) + ;
writeln(n, ' ', m);
for i := to n do
begin
x := i;
y := random(i - ) + ;
if (random() = ) then
writeln(x, ' ', y)
else
writeln(y, ' ', x);
end;
writeln;
for i := to m do
begin
a := min(random(maxn div m + add)+, n);
b := min(random(maxn div m + add)+, n);
make_list(n, a);
make_list(n, b);
end;
writeln;
end;
begin
assign(output, 'anst.in');
rewrite(output);
randomize;
t := random() + ;
for i := to t do
mkdata;
close(output);
end.
HDU6031 Innumerable Ancestors 倍增 - 题意详细概括 - 算法详解的更多相关文章
- BM算法 Boyer-Moore高质量实现代码详解与算法详解
Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...
- 机器学习经典算法详解及Python实现--基于SMO的SVM分类器
原文:http://blog.csdn.net/suipingsp/article/details/41645779 支持向量机基本上是最好的有监督学习算法,因其英文名为support vector ...
- 安全体系(二)——RSA算法详解
本文主要讲述RSA算法使用的基本数学知识.秘钥的计算过程以及加密和解密的过程. 安全体系(零)—— 加解密算法.消息摘要.消息认证技术.数字签名与公钥证书 安全体系(一)—— DES算法详解 1.概述 ...
- 第三十一节,目标检测算法之 Faster R-CNN算法详解
Ren, Shaoqing, et al. “Faster R-CNN: Towards real-time object detection with region proposal network ...
- 第二十九节,目标检测算法之R-CNN算法详解
Girshick, Ross, et al. “Rich feature hierarchies for accurate object detection and semantic segmenta ...
- A*算法详解链接
A星算法详解(个人认为最详细,最通俗易懂的一个版本) Introduction to the A* Algorithm 路径规划: a star, A星算法详解 实现A星算法
- KM算法详解[转]
KM算法详解 原帖链接:http://www.cnblogs.com/zpfbuaa/p/7218607.html#_label0 阅读目录 二分图博客推荐 匈牙利算法步骤 匈牙利算法博客推荐 KM算 ...
- ST算法详解
ST算法详解 Coded by Jelly_Goat. All rights reserved. 这个主要是说ST表的. 首先了解一下ST表是什么. 先来一个老套的情景带入. (假设所有的题目都是1s ...
- [Network Architecture]DPN(Dual Path Network)算法详解(转)
https://blog.csdn.net/u014380165/article/details/75676216 论文:Dual Path Networks 论文链接:https://arxiv.o ...
随机推荐
- gstreamer
Abstract Ogg Vorbis is a completely open, patent-free, professional audio encoding and streaming tec ...
- 前端 ---client、offset、scroll系列
client.offset.scroll系列 1.client系列 代码如下: <!DOCTYPE html> <html> <head> <meta c ...
- word发布博客
无向图双连通部件(双连通分量) 关节点和桥边的定义: 双连通部件的性质 每一个双连通部件应该包含至少两个顶点,除非整个无向图只包含一个顶点 如果两个双连通部件包含同一个顶点,那么这个共有的顶点 ...
- mysql installer gentask怎么关闭
1 前言 安装mysql community版本的,可能经常会看到mysql installer gentask console框出现,有时候甚烦,我们并不需要它经常检测更新. 2 解决方案 开始/附 ...
- 玩转EhCache之最简单的缓存框架
二.主要特性 快速: 简单: 多种缓存策略: 缓存数据有两级:内存和磁盘,因此无需担心容量问题: 缓存数据会在虚拟机重启的过程中写入磁盘: 可以通过 RMI.可插入 API 等方式进行分布式缓存: 具 ...
- 使用VW时,图片的问题
在项目中,使用了VW适配,给图片直接设置了width和height,浏览器模拟正常,在手机上就不显示 解决办法是:在图片外面包一层div,设置width和height,然后图片设置width:100% ...
- PID控制器开发笔记之六:不完全微分PID控制器的实现
从PID控制的基本原理我们知道,微分信号的引入可改善系统的动态特性,但也存在一个问题,那就是容易引进高频干扰,在偏差扰动突变时尤其显出微分项的不足.为了解决这个问题人们引入低通滤波方式来解决这一问题. ...
- 【转载】中文输入法下onKeyPress不能触发的问题
onKeypress---->oninput https://segmentfault.com/a/1190000008820968
- kali linux 更新问题
1.使用一次更新和升级软件替换 apt-get install && apt -y full -upgrade 之后使用 reboot重启 系统,重启之后 再次使用命令 ap ...
- python(10): xlsxwriter模块
import xlsxwriter as writer 注意: xlsxwriter 只能创建新文件,不可以修改原有文件.如果创建新文件时与原有文件同名,则会覆盖原有文件. import xlsxwr ...