題意:
給出一些樹:有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;
}