LCA专题
标签(空格分隔): LCA
我的个人网站挂了,最近就先用这个来写博客吧。以后争取在这个网站写一些与OI无关的个人爱好的东西。
题目来源:code[VS]
倍增--在线算法
用 $f[i][j]$ 记录从 $i$ 向上跳 $2^j$ 次会跳到的位置。需 $O(nlog(n))$ 的预处理与 $O(mlog(n))$ 的查询。具体如下:
//code[VS] P1036 LCA
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Edge
{
int from,to,next;
bool access;
Edge(int form=0,int to=0,int next=0,bool access=true):from(from),to(to),next(next),access(access) {}
}e[60100];
int depth[30100],f[30100][25],v[25],pre[30100];
int n;
int c[30100];
void bfs(int s)
{
c[1] = s; depth[s] = 1;
int head = 1,tail = 1;
while (head<=tail)
{
int x = c[head++];
int v = pre[x];
while (v)
{
if (e[v].access)
{
depth[e[v].to] = depth[x] + 1;
c[++tail] = e[v].to;
f[e[v].to][0] = x;
e[v^1].access = false;
}
v = e[v].next;
}
}
}
void prepare()
{
v[0] = 1;
for (int j = 1; j<=20; j++)
for (int i = 1; i<=n; i++)
{
f[i][j] = f[f[i][j-1]][j-1];
v[j] = 2*v[j-1];
}
}
int LCA(int x,int y)
{
int ans=0;
if (depth[x] < depth[y]) swap(x,y);
for (int i = 20;depth[x]>depth[y];i--)
if (depth[f[x][i]] >= depth[y])
{
ans += v[i];
x = f[x][i];
}
if (x==y) return ans;
for (int i = 20; i>=0; i--)
if (f[x][i] != f[y][i])
{
x = f[x][i];
y = f[y][i];
ans += v[i]*2;
}
ans += 2;
return ans;
}
int main()
{
memset(pre,0,sizeof(pre));
scanf("%d",&n);
for (int i = 1; i<n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
e[2*i] = Edge(x,y,pre[x],true);
pre[x] = 2*i;
e[2*i+1] = Edge(y,x,pre[y],true);
pre[y] = 2*i+1;
}
bfs(1);
prepare();
int m;
scanf("%d",&m);
int x,y=1,ans=0;
for (int i = 1; i<=m; i++)
{
x = y;
scanf("%d",&y);
ans += LCA(x,y);
}
printf("%d",ans);
}
Tarjan--离线算法
对于这么一个玄学的算法,我不想说太多。。。用并查集进行维护,可以证明,每当搜到 $x$ 时,与之对应的 $y$ 所在集合的祖先一定为这两点的LCA。
//code[VS] P1036 LCA
#include <cstdio>
#include <cstring>
struct Edge
{
int from,to,next;
bool access;
Edge(int from=0,int to=0,int next=0,bool access=true):from(from),to(to),next(next),access(access) {}
}e[60100];
struct Query
{
int point,next;
Query(int point=0,int next=0):point(point),next(next) {}
}q[60100];
//fa[i]记录i的父亲,f[i]记录i指向的第一条边,fq[i]记录i指向的第一个查询
int fa[30100],f[30100],fq[30100],depth[30100];
int ans=0; //记录答案
bool b[30100];
int c[30100];
void bfs(int s) //通过广搜计算出深度与边的方向
{
c[1] = s; depth[s] = 1;
int head = 1,tail = 1;
while (head<=tail)
{
int x = c[head++];
int v = f[x];
while (v)
{
if (e[v].access)
{
e[v^1].access = false; //将该边的反向边设为false
depth[e[v].to] = depth[x] + 1;
c[++tail] = e[v].to;
}
v = e[v].next;
}
}
}
int find(int x)
{
return x==fa[x]?x:fa[x]=find(fa[x]);
}
void Union(int x,int y)
{
int fy = find(y);
fa[fy] = x;
}
void Tarjan_LCA(int x)
{
fa[x] = x; //以x创建一个集合
int v = f[x];
while (v) //循环x的临边
{
if (e[v].access) //如果该边为正方向(即指向儿子)
{
Tarjan_LCA(e[v].to);
Union(x,e[v].to); //将x的子树与x合并
}
v = e[v].next;
}
b[x] = true; //设置该点已走过(必须在处理完儿子后设置,否则会有重复计算)
v = fq[x];
while (v) //处理关于x点的查询
{
if (b[q[v].point]) //如果另一点已走过 花费=a点深度+b点深度-2*LCA(a,b)的深度
ans = ans + ( depth[x] + depth[q[v].point] - 2*depth[find(q[v].point)] );
v = q[v].next;
}
}
int main()
{
memset(b,false,sizeof(b));
memset(f,0,sizeof(f));
memset(fq,0,sizeof(fq));
int n;
scanf("%d",&n);
for (int i = 1; i<n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
e[2*i] = Edge(x,y,f[x],true);
f[x] = 2*i;
e[2*i+1] = Edge(y,x,f[y],true);
f[y] = 2*i+1;
}
int m;
scanf("%d",&m);
int x,y=1;
for (int i = 1; i<=m; i++) //因为不知先查询到哪个点所以要存储双向变
{
x = y;
scanf("%d",&y);
q[i*2-1] = Query(y,fq[x]);
fq[x] = 2*i-1;
q[2*i] = Query(x,fq[y]);
fq[y] = 2*i;
}
bfs(1);
Tarjan_LCA(1);
printf("%d",ans);
}
LCA专题的更多相关文章
- 在线倍增法求LCA专题
1.cojs 186. [USACO Oct08] 牧场旅行 ★★ 输入文件:pwalk.in 输出文件:pwalk.out 简单对比时间限制:1 s 内存限制:128 MB n个被自 ...
- HDU 2586——How far away ?
Time limit 1000 ms Memory limit 32768 kB Description There are n houses in the village and some bidi ...
- 专题训练之LCA
推荐几个博客:https://www.cnblogs.com/JVxie/p/4854719.html Tarjan离线算法的基本思路及其算法实现 https://blog.csdn.net/shah ...
- LCA(最近公共祖先)专题(不定期更新)
Tarjan(离线)算法 思路: 1.任选一个点为根节点,从根节点开始. 2.遍历该点u所有子节点v,并标记这些子节点v已被访问过. 3.若是v还有子节点,返回2,否则下一步. 4.合并v到u上. 5 ...
- bryce1010专题训练——LCA
1.Targan算法(离线) http://poj.org/problem?id=1470 /*伪代码 Tarjan(u)//marge和find为并查集合并函数和查找函数 { for each(u, ...
- poj3728 倍增法lca 好题!
lca的好题!网上用st表和离线解的比较多,用树上倍增也是可以做的 不知道错在哪里,等刷完了这个专题再回来看 题解链接https://blog.csdn.net/Sd_Invol/article/de ...
- SPOJ 10628. Count on a tree (树上第k大,LCA+主席树)
10628. Count on a tree Problem code: COT You are given a tree with N nodes.The tree nodes are number ...
- POJ 1330 Nearest Common Ancestors (LCA,倍增算法,在线算法)
/* *********************************************** Author :kuangbin Created Time :2013-9-5 9:45:17 F ...
- POJ 1470 Closest Common Ancestors (LCA,离线Tarjan算法)
Closest Common Ancestors Time Limit: 2000MS Memory Limit: 10000K Total Submissions: 13372 Accept ...
随机推荐
- web服务器内置对象,或者说是ServletAPI的实例
final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; // ...
- Ajax大文件切割传输
文件上传服务器的大小是一定的,所以大文件可以切割成小文件,依次 传输,然后再拼接切割文件上传,用同步方式传输,为了防止异步传输 中多个块同时传输,文件拼接错误,导致文件损坏 前端页面 <!doc ...
- canvas-js贝塞尔曲线代码在线生成工具
详细内容请点击 canvas贝塞尔曲线代码在线生成工具 可以快速生成二次.三次贝塞尔曲线的源码生成器,方便经常使用到canvas画图的同学使用,可以直接预览效果随意画出自己想要的图像. 生成源码效果预 ...
- 行列转换 pivot
select * from ( select isnull(c.type,'其他') type,d from ( select ID,Record_code,code,day(thedate) d f ...
- 20150311—html中iframe(转发)
JS实现iframe框架页面跳转和刷新 一.js方式的页面跳转 1.window.location.href方式 <script language="javascript" ...
- JavaScript之Function函数深入总结
整理了JavaScript中函数Function的各种,感觉函数就是一大对象啊,各种知识点都能牵扯进来,不单单是 Function 这个本身原生的引用类型的各种用法,还包含执行环境,作用域,闭包,上下 ...
- 10款精美的HTML5表单登录联系和搜索表单
1.HTML5/CSS3仿Facebook登录表单 这款纯CSS3发光登录表单更是绚丽多彩.今天我们要分享一款仿Facebook的登录表单,无论从外观还是功能上说,这款登录表单还是挺接近Faceboo ...
- 《HTML5与CSS3基础教程》学习笔记 ——Four Day
第十六章 1. 输入和元素 电子邮件框 <input type="email"> 搜索框 <input type="search"> ...
- Excel中 设置使得每行的颜色不一样
在编写测试案例的时候,众多的excel行看的眼睛花花的,这里给出一个小技巧,设置Excel的每行显示的颜色不一样,最终的效果如下: 具体操作: 1. Ctrl+A全选所有表格区域 ...
- 火狐谷歌浏览器Json查看插件
1.搜: Firefox的JSON插件 参考: Chrome/FireFox浏览器下处理JSON的插件_Bruce_新浪博客 JSONView :: Firefox 附加组件 但是后来去发现没用: 打 ...