Algorithm Note 7 Kruskal重构树算法/再谈NOIP2013货车运输

//对Kruskal算法的介绍以sdsc课件为基础,原作者为djh老师。

Kruskal 重构树算法是一种基于 Kruskal 的变形。

在使用并查集合并两个集合 (两棵树) 的时候,我们新建一个点作为树根,点权为连接两个集合的边的边权。

我们考虑这样得到的一棵树会有什么性质。

1. 原图中的所有节点都是叶子。因为合并过程中只有新结点能作为父节点。

2. 总共有 O(n) 个节点。

3. 是一棵二叉树。

4. 如果把叶子节点的权值看作 0,那么满足大根堆的性质。显然,由于按边权排序,新加入的点权大于一定两个子结点。

5. 最小生成树上两点间路径边权的最大值是他们重构树上LCA的点权大小。因为当两个点首次在重构树上联通时也在原生成树上联通,它们之间路径的最大值一定时当时的根,也就是LCA。

 

代码实现

 1 for(int i = 1; i <= n; ++i) fa[i] = i;
 2 sort(e + 1, e + 1 + m);
 3 int node = n;
 4 for(int i = 1; i <= m; ++i) {
 5     int fu = getf(e[i].u), fv = getf(e[i].v);
 6     if(fu == fv) continue;
 7     val[++ node] = e[i].c;
 8     fa[node] = fa[u] = fa[v] = node;
 9     add(node, u); add(node, v);
10     cnt ++;
11     if(cnt == n - 1) break;
12 }

然后课件上放了这个模板题和NOI2018 归程。

 不过模版题没处交,归程咋都调不出来,于是我想起以前写的一个题,NOIP2013(洛谷P1967)货车运输。

https://www.luogu.com.cn/problem/P1967

 题意:给定一张地图,n个地点m条道路,每段道路有最大限重。q次询问从u到v两地之间货车载重量的最大值,如果两地不能互达输出-1。

所以这不就是模板题吗。。。

不过我还是调了很久。在sdsc刚养成all in stl的习惯,经常犯一些i <= g[u].size()之类的错误。

然后注意这题得到一个森林,不能直接dfs(1, 0),而应该

 

for (int i = 1; i <= cnt; i++) {
    if (!vis[i]) {
      int f = find(i);
      dfs(f, 0);
    }
  }

 

为什么是对的呢?因为建重构树的过程保证了find(i)为重构树的根。

以下是完整代码。

 

#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 6;
struct edge {
  int u, v, w;
  bool operator < (const edge &x) {
    return w > x.w;
  }
} e[N];
vector<int> g[N];
int n, m, q, cnt, fa[N], vis[N], val[N], d[N], f[N][30];
int find(int x) {
  return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void kruskal() {
  sort(e + 1, e + m + 1);
  for (int i = 1; i <= n; i++) fa[i] = i; 
  for (int i = 1; i <= m; i++) {
    int fu = find(e[i].u), fv = find(e[i].v);
    if (fu != fv) {
      val[++cnt] = e[i].w;
      fa[cnt] = fa[fu] = fa[fv] = cnt;
      g[cnt].push_back(fu); g[cnt].push_back(fv); g[fu].push_back(cnt); g[fv].push_back(cnt);
    }
  }
}
void dfs(int u, int fa) {
  d[u] = d[fa] + 1, f[u][0] = fa, vis[u] = 1;
  for (int i = 1; (1 << i) <= d[u]; i++) 
    f[u][i] = f[f[u][i-1]][i-1];
  for (int i = 0; i < g[u].size(); i++) {
    int v = g[u][i];
    if (v != fa) dfs(v, u);
  }
}
int lca(int x, int y) {
  if (d[x] < d[y]) swap(x, y);
  int k = d[x] - d[y];
  for (int i = 25; i >= 0; i--)
    if ((1 << i) & k) x = f[x][i];
  if (x == y) return x;
  for (int i = 25; i >= 0; i--)
    if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
  return f[x][0];  
}
int main() {
  cin >> n >> m; cnt = n;
  for (int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v >> e[i].w;
  kruskal();
  for (int i = 1; i <= cnt; i++) {
    if (!vis[i]) {
      int f = find(i);
      dfs(f, 0);
    }
  }
  cin >> q;
  while (q--) {
    int u, v;
    cin >> u >> v;
    if (find(u) != find(v)) cout << -1 << endl;
    else cout << val[lca(u, v)] << endl;
  }
  return 0;
}

 

 
posted @ 2020-08-17 21:45  _vv123  阅读(136)  评论(0编辑  收藏  举报