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 ...
随机推荐
- Android布局技巧
postInvalidate(); 界面刷新 一个list元素可能默认为48pxandroid:visibility="invisibl ...
- VB.NET 小程序 2
Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click ...
- 前端工作流程自动化——Grunt/Gulp 自动化
什么是自动化 先来说说为什么要自动化.凡是要考虑到自动化时,你所做的工作必然是存在很多重复乏味的劳作,很有必要通过程序来完成这些任务.这样一来就可以解放生产力,将更多的精力和时间投入到更多有意义的事情 ...
- spring3+struts2+hibernate3整合出现的问题,No mapping found for dependency [type=java.lang.String, name='struts.objectFactory.spring.enableAopSupport']
七月 11, 2016 3:49:24 下午 org.apache.tomcat.util.digester.SetPropertiesRule begin警告: [SetPropertiesRule ...
- 【学习笔记】【C语言】进制
1. 什么是进制 是一种计数的方式,数值的表示形式 2. 二进制 1> 特点:只有0和1,逢2进1 2> 书写格式:0b或者0b开头 3> 使用场合:二进制指令\二进制文件,变量在内 ...
- DTCMS使用ajax局部刷新
动力启航的DTCMS代码遇到的问题: 前台post请求: $.ajax({ type: "POST", url: sendUrl, dataType: "json&quo ...
- UI1_Calayer
// // ViewController.m // UI1_Calayer // // Created by zhangxueming on 15/7/2. // Copyright (c) 2015 ...
- SCP服务实现Linux交互
SCP服务实现Linux交互 在实际工作中,我们可以使用scp服务器进行Linux与Linux之间的信息交互. 基本指令: scp 本地文件 远程文件 scp ...
- 【leetcode】368. Largest Divisible Subset
题目描述: Given a set of distinct positive integers, find the largest subset such that every pair (Si, S ...
- 安装Java EE失败,解决方案
笔者安装Java EE(版本是java_ee_sdk-7-jdk7-windows-x64-ml.exe)时,遇到错误提示提示"Could not find the required ver ...