传送门

•题意

  在一个包含 n 个节点 m 条边的森林中;

  有 q 次询问,每次询问求解两点间的最短距离;

  如果这两点不联通,输出 "Not connected";

•题解1

  树上任意两点间的最短距离就是最近公共祖先分别到这两点的距离和;

  那么这个问题就被转化成了LCA问题。

  因为有多棵树,所以,对于每棵树,都提前预处理出 $dis,dep$;

  并通过并查集判断询问的两点是否联通;

•Code

 #include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn=1e4+; int n,m,q;
int num;
int head[maxn];
struct Edge
{
int to;
ll w;
int next;
}G[maxn<<];
void addEdge(int u,int v,ll w)
{
G[num]={v,w,head[u]};
head[u]=num++;
}
vector<int >V[maxn];
/**
fa[i][j]:节点j沿着其父结点向上走2^i步所到的节点(超过根节点时记为-1)
///dis[i]:节点i的与根节点的距离
///dep[i]:节点i的深度,根节点深度为0
*/
struct LCA
{
int fa[][maxn];
ll dis[maxn];
ll dep[maxn];
void DFS(int u,int f,ll Dis,ll Dep)
{
fa[][u]=f;///节点u向上走2^0步来到的节点便是其父节点
dis[u]=Dis;
dep[u]=Dep;
for(int i=head[u];~i;i=G[i].next)
{
int v=G[i].to;
ll w=G[i].w;
if(v != f)
DFS(v,u,Dis+w,Dep+);
}
}
void Init()
{
for(int i=;i <= n;++i)
{
if(V[i].empty())
continue;
///预处理出每棵树的dis,dep,fa
DFS(V[i][],-,,);
for(int k=;k <= ;++k)
for(int j=;j < V[i].size();++j)
{
int u=V[i][j];
if(fa[k-][u] == -)
fa[k][u]=-;
else
fa[k][u]=fa[k-][fa[k-][u]];
}
}
}
int lca(int u,int v)///返回u,v的最近公共祖先
{
if(dep[u] > dep[v])
swap(u,v); for(int i=;i <= ;++i)
if((dep[v]-dep[u])>>i&)
v=fa[i][v];
if(u == v)
return u; for(int i=;i >= ;--i)
if(fa[i][u] != fa[i][v])
{
u=fa[i][u];
v=fa[i][v];
}
return fa[][u];
}
}_lca;
struct Set
{
int fa[maxn];
void Init()
{
for(int i=;i <= n;++i)
fa[i]=i;
}
int Find(int x)
{
return x == fa[x] ? x:fa[x]=Find(fa[x]);
}
void Union(int x,int y)
{
x=Find(x);
y=Find(y);
if(x != y)
fa[x]=y;
}
}_set;
void Solve()
{
for(int i=;i <= n;++i)///将属于同一颗树的节点存在_set.fa[i]中
V[_set.Find(i)].push_back(i);///并查集查找i的祖先节点用Find()
_lca.Init(); while(q--)
{
int u,v;
scanf("%d%d",&u,&v);
if(_set.Find(u) != _set.Find(v))///判断u,v是否属于同一棵树用Find()
puts("Not connected");
else
{
int x=_lca.lca(u,v);
ll ans=_lca.dis[u]+_lca.dis[v]-*_lca.dis[x];
printf("%lld\n",ans);
}
}
}
void Init()
{
num=;
for(int i=;i <= n;++i)
{
head[i]=-;
V[i].clear();
}
_set.Init();
}
int main()
{
// freopen("C:\\Users\\hyacinthLJP\\Desktop\\C++WorkSpace\\in&&out\\contest","r",stdin);
while(~scanf("%d%d%d",&n,&m,&q))
{
Init();
for(int i=;i <= m;++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addEdge(u,v,w);
addEdge(v,u,w);
_set.Union(u,v);
}
Solve();
}
return ;
}

基于二分的LCA

•题解2

  通过添加虚点将森林转化成一棵树;

  并以添加的虚点作为这棵树的根节点;

  对于询问操作,如果询问的两点的 $LCA$ 为虚点,那么这两点在原森林中不连通;

  这么做的话,只需处理一棵树的 $dis,dep,fa$;

•Code

 #include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn=1e4+; int n,m,q;
int num;
int head[maxn];
struct Edge
{
int to;
ll w;
int next;
}G[maxn<<];
void addEdge(int u,int v,ll w)
{
G[num]={v,w,head[u]};
head[u]=num++;
}
/**
fa[i][j]:节点j沿着其父结点向上走2^i步所到的节点(超过根节点时记为-1)
///dis[i]:节点i的与根节点的距离
///dep[i]:节点i的深度,根节点深度为0
*/
struct LCA
{
int fa[][maxn];
ll dis[maxn];
ll dep[maxn];
void DFS(int u,int f,ll Dis,ll Dep)
{
fa[][u]=f;///节点u向上走2^0步来到的节点便是其父节点
dis[u]=Dis;
dep[u]=Dep;
for(int i=head[u];~i;i=G[i].next)
{
int v=G[i].to;
ll w=G[i].w;
if(v != f)
DFS(v,u,Dis+w,Dep+);
}
}
void Init()
{
DFS(n+,-,,);
for(int k=;k <= ;++k)
for(int u=;u <= n+;++u)
if(fa[k-][u] == -)
fa[k][u]=-;
else
fa[k][u]=fa[k-][fa[k-][u]];
}
int lca(int u,int v)///返回u,v的最近公共祖先
{
if(dep[u] > dep[v])
swap(u,v); for(int i=;i <= ;++i)
if((dep[v]-dep[u])>>i&)
v=fa[i][v];
if(u == v)
return u; for(int i=;i >= ;--i)
if(fa[i][u] != fa[i][v])
{
u=fa[i][u];
v=fa[i][v];
}
return fa[][u];
}
}_lca;
struct Set
{
int fa[maxn];
void Init()
{
for(int i=;i <= n+;++i)
fa[i]=i;
}
int Find(int x)
{
return x == fa[x] ? x:fa[x]=Find(fa[x]);
}
void Union(int x,int y)
{
x=Find(x);
y=Find(y);
if(x != y)
fa[x]=y;
}
}_set;
void Solve()
{
_lca.Init(); while(q--)
{
int u,v;
scanf("%d%d",&u,&v);
int x=_lca.lca(u,v);
if(x == n+)
puts("Not connected");
else
{
ll ans=_lca.dis[u]+_lca.dis[v]-*_lca.dis[x];
printf("%lld\n",ans);
}
}
}
bool vis[maxn];
void Init()
{
num=;
for(int i=;i <= n+;++i)
{
head[i]=-;
vis[i]=false;
}
_set.Init();
}
int main()
{
// freopen("C:\\Users\\hyacinthLJP\\Desktop\\C++WorkSpace\\in&&out\\contest","r",stdin);
while(~scanf("%d%d%d",&n,&m,&q))
{
Init();
for(int i=;i <= m;++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addEdge(u,v,w);
addEdge(v,u,w);
_set.Union(u,v);
}
///定义虚节点 n+1
///将节点n+1与连接每棵树的某个节点
///每棵树只有一个节点与节点n+1相连,边仅加一次
for(int i=;i <= n;++i)
if(!vis[_set.Find(i)])///此处用Find(i)而不是用fa[i]
{
addEdge(n+,_set.Find(i),);
vis[_set.Find(i)]=true;
} Solve();
}
return ;
}

基于二分的LCA

hdu 2874(裸LCA)的更多相关文章

  1. hdu 2586(裸LCA)

    传送门 题意: 某村庄有n个小屋,n-1条道路连接着n个小屋(无环),求村庄A到村庄B的距离,要求是经过任一村庄不超过一次. 题解: 求出 lca = LCA(u,v) , 然后答案便是dist[u] ...

  2. hdu 2874(LCA)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2874 思路:近乎纯裸的LCA,只是题目给出的是森林,就要判断是否都在同一颗树上,这里我们只需判断两个子 ...

  3. HDU 2874 Connections between cities (LCA)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2874 题意是给你n个点,m条边(无向),q个询问.接下来m行,每行两个点一个边权,而且这个图不能有环路 ...

  4. HDU 2874 Connections between cities(LCA离线算法实现)

    http://acm.hdu.edu.cn/showproblem.php?pid=2874 题意: 求两个城市之间的距离. 思路: LCA题,注意原图可能不连通. 如果不了解离线算法的话,可以看我之 ...

  5. HDU 2874 Connections between cities(LCA(离线、在线)求树上距离+森林)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2874 题目大意:给出n个点,m条边,q个询问,每次询问(u,v)的最短距离,若(u,v)不连通即不在同 ...

  6. HDU 2874 Connections between cities(LCA+并查集)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=2874 [题目大意] 有n个村庄,m条路,不存在环,有q个询问,问两个村庄是否可达, 如果可达则输出 ...

  7. 【HDU 2874】Connections between cities(LCA)

    dfs找出所有节点所在树及到树根的距离及深度及父亲. i和j在一棵树上,则最短路为dis[i]+dis[j]-dis[LCA(i,j)]*2. #include <cstring> #in ...

  8. hdu 2874 Connections between cities [LCA] (lca->rmq)

    Connections between cities Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (J ...

  9. HDU 2874 Connections between cities(LCA Tarjan)

    Connections between cities [题目链接]Connections between cities [题目类型]LCA Tarjan &题意: 输入一个森林,总节点不超过N ...

随机推荐

  1. Gerrit日常维护记录

    Gerrit代码审核工具是个好东西,尤其是在和Gitlab和Jenkins对接后,在代码控制方面有着无与伦比的优势. 在公司线上部署了一套Gerrit系统,在日常运维中,使用了很多gerrit命令,在 ...

  2. js 时间戳转换为日期格式

     //将1525854409000类型的时间转换成“yyyy-MM-dd”或“yyyy-MM-dd hh:mm:ss”   //info.birthday是后台获取到的Date类型的出生日期数据, / ...

  3. Linux内核及分析 第二周 操作系统是如何工作的?

    计算机是如何工作的? 存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: 函数调用堆栈,高级语言得以运行的基础,只有机器语言和汇编语言的时候堆栈机制对于计算机来说并不那么重要,但有了高级语言及函 ...

  4. 在-for 循环里面如何利用ref 操作dom

    由于dom 元素是在渲染之后才能操作,所以如果想取到dom元素,要放到mounted()这个生命周期函数里面,并且还要用this.$nextTick(function () {})

  5. String系列-----String

    jdk源码学习之String,手动实现一个String package com.amazing.jdk.string_2017_12_31; import java.io.Serializable; ...

  6. Delphi/XE2 使用TIdHttp控件下载Https协议服务器文件[转]

    之前的一篇博文详细描述了使用TIdhttp控件下载http协议的文件,在我项目的使用过程中发现对于下载Https协议中的文件与Http协议的文件不同,毕竟Https在HTTP协议基础上增加了SSL协议 ...

  7. Docker Dockerfile指令

    Docker 可以通过 Dockerfile 的内容来自动构建镜像.Dockerfile 是一个包含创建镜像所有命令的文本文件,通过docker build命令可以根据 Dockerfile 的内容构 ...

  8. java中的equals和==

    下面是我看别人博客和java API总结的 首先得明确一个概念就是: == 的用法   ==比较对象在内存中的地址是否相等.如是是两个基本数据类型变量的比较则比较的是这两个变量值是否相等,若是比较两个 ...

  9. [转帖]一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系

    一文看懂web服务器.应用服务器.web容器.反向代理服务器区别与联系 https://www.cnblogs.com/vipyoumay/p/7455431.html 我们知道,不同肤色的人外貌差别 ...

  10. GS使用HTTPS登录的设置过程

    1. Windows 增加角色服务 服务器配置管理器, 添加角色服务 增加角色功能里面有: 证书颁发机构 证书颁发机构 web注册 2. AD CS配置 主要是next操作 独立ca 根证书 等 3. ...