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 ...
随机推荐
- 实战FFmpeg编译支持arm64(转)
App store要求上架的app必须支持arm64.而手中的ffmpeg还不支持arm64, 百度下ffmpeg支持arm64方法,网上有很多资料.其中一篇是使用脚本自动编译实现的.本文就是使用它的 ...
- 八、android jni 之C语言基础
*含义 1.乘法 3*5 2.定义指针变量 int * p://定义了一个名字叫p的变量,能够存放int数据类型的地址 3.指针运算符, //如果p是一个已经定义好的指针变量则*p表示以p的内容为地址 ...
- Python Opearte MS-SQL Use Pymssql
看到很多的开源数据库会用到MySQL,Python同样也使用,但是我已经习惯使用图形化界面,操作感极强的MS-SQL 看到Python也提供MS-SQL连接方式,需要用到PyMssql. 在Windo ...
- 通过带参数的Sql语句来实现模糊查询(多条件查询)
#region 通过带参数的Sql语句来实现模糊查询(多条件查询) StringBuilder sb = new StringBuilder("select * from books&quo ...
- Linux下用freetds连接mssql中文乱码的问题【参考1】
由于工作原因我们需要通过php访问我们以前的Sql Server 2005数据,所以就有了这篇文章的诞生.废话就少说了,做程序设计的最不喜欢兜圈子了.用简介步骤说明问题,往下看.系统: Linux ...
- php删除数组中相同的元素,只保留一个相同元素
<?php// 删除数组中相同元素,只保留一个相同元素function formatArray($array){sort($array);$tem = ”;$temarray = array() ...
- CTG
服务端:一.修改IP地址AIX上CICS REGION的目录/var/cics_regions/your_region_name/database/LD二.修改文件LD.stanza中的IP地址三. ...
- Ajax乱码问题
Ajax中文乱码问题 1.js代码 var userName=$("#userName").val(); var url = "user/login.action?u ...
- cass实体编码列表
地物名称 编码 图层 类别 参数一 参数二 实体类型 三角点 131100 KZD 20 gc113 3 SPECIAL,1 三角点分数线 131110 KZD 附 LINE 三角点高程注记 1311 ...
- App性能提升方法
总体思路:精简请求数 1.css sprit 图像拼合,将所有可拼接的所有图像拼接为一整张图像,然后再利用css中的position定位来处理,降低图片的请求数 2.懒加载:只渲染客户端用户可见区域[ ...