hdu 2874 Connections between cities LCA || dfs+并查集

题意: 

给出一些树:有n(10000), m条边(10000), c(1000000)个询问,每个询问a b要求找到a到b的最短距离,如果ab不在同一棵树,输出-1.

题解:

此题就是找两个节点的最近公共祖先,则两者的距离就是a和b到根节点的距离之和 - 2倍公共祖先到根节点的距离。可用离线的LCA算法,其实就是dfs+并查集,下面分析一下离线的LCA, 我是看了原理后,自己敲出的代码。

LCA:

离线的LCA,其实就是说,只用一次dfs遍历所有的节点,就可以找出任意两个节点的距离,但是问题是,由于节点太多,无法保存任意两个节点的距离,所以在具体应用中,需要先保存询问,然后在dfs的过程中,只需要保存询问的节点的答案即可。LCA: 其实就是先序遍历这棵树,然后当访问完一个节点后,就把这个节点和他的父节点用并查集并到一起,那么当访问到要询问的节点U后,只需要判断询问边的另一个节点V是否被访问过了,如果被访问过了,则V的父节点就是两者的最近公共祖先。

细节:

在本题中,可能不止一颗树,所以在处理过程中,可以增加一个虚拟的0节点,作为所有节点的根节点,这样,如果访问节点的父节点是0节点,就代表这两个节点不属于同一棵树,则他们应该输出-1.

代码:

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int maxn = 1e4 + 10;
int n, m, c, fa[maxn], ans[1000000+10], vis[maxn], dist[maxn];
struct Node{
    int v, d;
    Node(int v = 0, int d = 0) : v(v), d(d){}
};
vector<Node> G[maxn];
vector<Node> query[maxn];

int findSet(int x)
{
    if (x != fa[x]) {
        fa[x] = findSet(fa[x]);
    }
    return fa[x];
}
void init()
{
    for (int i = 0; i <= n; i++)
    {
        fa[i] = i;
        dist[i] = 0;
        vis[i] = 0;
        G[i].clear();
        query[i].clear();
    }
    for (int i = 0; i < m; i++)
    {
        int u, v, d; scanf("%d%d%d", &u, &v, &d);
        G[u].push_back(Node(v, d));
        G[v].push_back(Node(u, d));
        int fu = findSet(u), fv = findSet(v);
        if (fu != fv) {
            fa[fu] = fv;
        }
    }
    for (int i = 1; i <= n; i++) {
        int fi = findSet(i);
        if (fa[fi] == i) {
            G[0].push_back(Node(i, 0));
        }
    }
    for (int i = 0; i <= n; i++) {
        fa[i] = i;
    }
    for (int i = 0; i < c; i++) {
        int u, v; scanf("%d%d", &u, &v);
        ans[i] = -1;
        query[u].push_back(Node(v, i));
        query[v].push_back(Node(u, i));
    }
}
void print()
{
    for (int i = 0; i < c; i++) {
        if (ans[i] != -1) {
            printf("%d\n", ans[i]);
        }
        else printf("Not connected\n");
    }
}
void dfs(int u)
{
    vis[u] = 1;
    for (int i = 0; i < query[u].size(); i++)
    {
        int fv = findSet(query[u][i].v);
        if (vis[query[u][i].v] && fa[fv] != 0)
        {
            ans[query[u][i].d] = dist[query[u][i].v] + dist[u] - 2*dist[fa[fv]];
        }
    }
    for (int i = 0; i < G[u].size(); i++)
    {
        if (vis[G[u][i].v]) continue;
        
        dist[G[u][i].v] = dist[u] + G[u][i].d;
        dfs(G[u][i].v);
        int fv = findSet(G[u][i].v);
        int fu = findSet(u);
        fa[fv] = fa[fu];
    }
}
int main()
{
//    freopen("/Users/apple/Desktop/in.txt", "r", stdin);
    while (scanf("%d%d%d", &n, &m, &c) != EOF)
    {
        init();
        dfs(0);
        print();
    }
    
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值