去博客园看该题解

题目

查看原题 - 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 倍增 - 题意详细概括 - 算法详解的更多相关文章

  1. BM算法  Boyer-Moore高质量实现代码详解与算法详解

    Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...

  2. 机器学习经典算法详解及Python实现--基于SMO的SVM分类器

    原文:http://blog.csdn.net/suipingsp/article/details/41645779 支持向量机基本上是最好的有监督学习算法,因其英文名为support vector  ...

  3. 安全体系(二)——RSA算法详解

    本文主要讲述RSA算法使用的基本数学知识.秘钥的计算过程以及加密和解密的过程. 安全体系(零)—— 加解密算法.消息摘要.消息认证技术.数字签名与公钥证书 安全体系(一)—— DES算法详解 1.概述 ...

  4. 第三十一节,目标检测算法之 Faster R-CNN算法详解

    Ren, Shaoqing, et al. “Faster R-CNN: Towards real-time object detection with region proposal network ...

  5. 第二十九节,目标检测算法之R-CNN算法详解

    Girshick, Ross, et al. “Rich feature hierarchies for accurate object detection and semantic segmenta ...

  6. A*算法详解链接

    A星算法详解(个人认为最详细,最通俗易懂的一个版本) Introduction to the A* Algorithm 路径规划: a star, A星算法详解 实现A星算法

  7. KM算法详解[转]

    KM算法详解 原帖链接:http://www.cnblogs.com/zpfbuaa/p/7218607.html#_label0 阅读目录 二分图博客推荐 匈牙利算法步骤 匈牙利算法博客推荐 KM算 ...

  8. ST算法详解

    ST算法详解 Coded by Jelly_Goat. All rights reserved. 这个主要是说ST表的. 首先了解一下ST表是什么. 先来一个老套的情景带入. (假设所有的题目都是1s ...

  9. [Network Architecture]DPN(Dual Path Network)算法详解(转)

    https://blog.csdn.net/u014380165/article/details/75676216 论文:Dual Path Networks 论文链接:https://arxiv.o ...

随机推荐

  1. oracle数据库链路和同义词

    Oracle数据库链路的建立和使用 先来一个例子: --创建数据库链路create   public   database link xaffdblink connect to zdcl identi ...

  2. Visual Studio Code使用指南

    简介 Visual Studio Code是一款免费开源的现代化轻量级代码编辑器,支持几乎所有主流的开发语言的语法高亮.智能代码补全.自定义热键.括号匹配.代码片段.代码对比 Diff.GIT 等特性 ...

  3. Mybatis调用PostgreSQL存储过程实现数组入参传递

    注:本文来源于 < Mybatis调用PostgreSQL存储过程实现数组入参传递  > 前言 项目中用到了Mybatis调用PostgreSQL存储过程(自定义函数)相关操作,由于Pos ...

  4. swift项目初始化并添加忽略文件Swift.ignore

    1 先去GitHub上去把最新的忽略文件下载下载 https://github.com/github/gitignore 2 然后找到Swift.gitignore  把里面的 pod 前面的# 删除 ...

  5. numpy:dot与multiply

    http://blog.csdn.net/iamzhangzhuping/article/details/52370241

  6. verilog-testbench 时钟和复位模板

    /********************************************* ** Clocks & Reset ******************************* ...

  7. javaScript遍历对象、数组总结

        javaScript遍历对象总结 1.使用Object.keys()遍历 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性). var obj = {'0':'a ...

  8. LeetCode(123):买卖股票的最佳时机 III

    Hard! 题目描述: 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你最多可以完成 两笔 交易. 注意: 你不能同时参与多笔交易(你必 ...

  9. laravel 多对多关联 attach detach sync

    用户表和角色表,多对多关联,一个用户有多个角色,一个角色属于多个用户 添加多对多关联 attach: 给1号用户添加1号角色,并把关联表的column字段赋值为$value,后边的数组需要的时候再添加 ...

  10. C#关于线程的问题

    1.通过System.threading.Thread类可以创建新的线程,并在线程堆栈中运行静态和动态的实例,可以通过Thread类的构造方法传递一个无参数,并且不返回的委托, class Progr ...