传送门

•题意

  在一个包含 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. 最好使用%f输出浮点数据,acm

    今天做题的时候发现使用%lf输出的时候总是wrong,而一旦改成%f就ac了,询问学长后知道,不要用%lf输出,浮点都用%f 然而我还是有疑惑,如果%f容不下输出的数据怎么办呢? 于是我就去百度 原来 ...

  2. HDU 3537 Daizhenyang's Coin

    链接 [http://acm.hdu.edu.cn/showproblem.php?pid=3537] 题意 题意:已知一排硬币中有n个硬币正面朝上,输入正面朝上的硬币的位置ai.两人轮流操作, 每次 ...

  3. C. Oh Those Palindromes

    题意 给以一个字符串,让你重排列,使得回文子串的数目最多 分析 对于一个回文串,在其中加入一些字符并不会使回文子串的个数增加,所以对于相同的字符一起输出即可,我是直接排序 代码 #include< ...

  4. 【补充】第一次个人项目出现的bug

    新程序包下载(密码:4kp6) >>>>>直接上代码,问题出在随机分数的生成上,确实出现了一些非常鱼唇的错误,不过已经提交了就没办法了,在这里发出来仅供参考吧: 修改前: ...

  5. 个人作业Week1

    一.<构建之法>提问 1.需求是什么?需求的规范需要明确吗? 2.一个人开发效率非常高,多人开发,个人效率随团队人数上升而直线下降,我们一般需要将大项目拆为小项目,使协作耦合产生的效率负影 ...

  6. 个人项目Individual Project:迷宫求解

    源码的github链接:           https://github.com/zhangxue520/test 1.1问题描述: a.问题描述:以一个m * n的长方阵表示迷宫,0和1分别表示迷 ...

  7. CSS里Postion几个取值relative、absolute、static、fixed的区别和用法

    ---恢复内容开始--- static:静态定位,也是postion的默认值,没有定位,元素出现在正常的流中,忽略top\bottom\left\right或者z-index声明. relative: ...

  8. Apache的Thrift引发的编译思考

    最近偶然看到了Apache的Thrift,感觉有点像Corba架构后的变种(赞一个,Facebook真伟大).WSDL能生成C#和Java的(SOAP标准接口,做WebService都用过).Corb ...

  9. Android提供的layout文件存放位置

    在编程的过程中,会用到android.R.layout下的一些常量.与这些常量对应的,Android提供了对应点的layout布局文件. android.jar中有对应的xml文件,但是打开的时候通常 ...

  10. React 表单refs

    <!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...