kruskal 重构树

(kruskal) 重构树

算法

其实这个算法就是把并查集之间的合并放到树上体现出来了,并没有什么难的地方。
我们对于正常的 (kruskal) 的并查集的过程,只是在需要合并的时候,建一个新点,新点的权值是原来边的权值,然后对于原来的两个联通块所代表的点连边。
下面大概说一下重构树的性质:

  1. 这棵树是一棵二叉树,且具有堆的性质。
  2. 树上除了叶子节点是原来的点,其余的点都是新建的点,且都有权值。
  3. 两个点 (u)(v)(lca) 的点权就对应着它们最小生成树上的路径上的最大值(瓶颈)。

然后好像就没什么了,一般这种题可能都比较套路。

例题

这里以NOI2018 归程为例题。

这个应该算是比较套路的题目吧。
水位线的限制让我很显然的就想到了重构树,所以直接建一棵出来就可以了,本题的树是一棵小根堆。

然后假设我们找到了满足题目水位线限制的最小节点,那么这个节点的子树都是可以坐车互相到达的,那么我们只要找到这里面的一个点,使其到达一号点的距离最短就行了。

所以大体流程就已经出来了,我们需要跑一个最短路,还需要建出重构树,还需要倍增找到满足条件的深度最小的点,然后这道题就做完了。

作为正规比赛里第一道卡 (spfa) 的题,我们还是老老实实的写 Dij 吧。(可能优化过的 (spfa) 也可以过)

然后放个本人的代码:

// by longdie
#include <bits/stdc++.h>
#define cl(x) memset(x, 0, sizeof(x))
using namespace std; // 最短路 + 并查集 + 倍增
const int N = 2e5 + 5, inf = 0x3f3f3f3f;
inline int read(int s = 0, int f = 1, char ch = getchar()) {
    while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); }
    while(isdigit(ch)) { s = s*10 + ch - '0', ch = getchar(); }
    return s * f;
}
vector<int> g[N<<2];
int n, m, head[N], cnt, dis[N], vis[N], fa[N<<2], tot, val[N<<2], f[N<<2][21], Min[N<<2], dep[N<<2], mx[N<<2][21];
struct edge1 { int to, next, w; } e[N << 2];
inline void add1(int x, int y, int z) {
    e[++cnt] = (edge1){y, head[x], z}, head[x] = cnt;
}
struct edge2 { int x, y, z; } a[N << 2];
struct node {
    int id, val;
    node() {}
    node(int a, int b) { id = a, val = b; }
    friend bool operator < (const node &a, const node &b) { return a.val > b.val; }
}; priority_queue<node> q;  
void Dij() {
    memset(dis, 0x3f, sizeof(dis)), memset(vis, 0, sizeof(vis));
    dis[1] = 0, q.push(node(1, 0));
    while(q.size()) {
        node t = q.top(); q.pop(); int u = t.id;
        if(vis[u]) continue; 
        vis[u] = 1;
        for(register int i = head[u], v; i; i = e[i].next) {
            v = e[i].to;
            if(dis[v] > dis[u] + e[i].w) {
                dis[v] = dis[u] + e[i].w;
                q.push(node(v, dis[v]));
            }
        }
    }
}
inline bool cmp(edge2 a, edge2 b) { return a.z > b.z; }
inline int get_fa(int x) {
    return x == fa[x] ? x : fa[x] = get_fa(fa[x]);
}
inline void merge(int x, int y, int z) {
    int fx = get_fa(x), fy = get_fa(y);
    ++tot, val[tot] = z;
    g[tot].push_back(fx), g[tot].push_back(fy);
    fa[fx] = tot, fa[fy] = tot;
}
void dfs0(int u) {
    if(u <= n) Min[u] = dis[u];
    else Min[u] = inf;
    for(register int i = 0; i < g[u].size(); ++i) {
        int v = g[u][i];
        dep[v] = dep[u] + 1, f[v][0] = u, mx[v][0] = val[u];
        for(register int j = 1; j <= 19; ++j) {
            f[v][j] = f[f[v][j-1]][j-1];
            mx[v][j] = min(mx[v][j-1], mx[f[v][j-1]][j-1]);
        }
        dfs0(v);
        Min[u] = min(Min[u], Min[v]);
    }
}
int query(int u, int v) {
    for(register int i = 19; i >= 0; --i) {
        if(mx[u][i] > v) u = f[u][i];
    }
    return Min[u];
}
void init() {
    cl(head), cl(val), tot = cnt = 0, cl(f), cl(Min), cl(mx);
    for(register int i = 1; i <= n * 4; ++i) g[i].clear();
}
signed main() {
    int T = read();
    while(T--) {
        n = read(), m = read();
        init(), tot = n;
        for(register int i = 1, x, y, z, w; i <= m; ++i) {
            x = read(), y = read(), z = read(), w = read();
            add1(x, y, z), add1(y, x, z);
            a[i] = (edge2){x, y, w};
        }
        Dij();
        sort(a + 1, a + m + 1, cmp);
        for(register int i = 1; i <= n << 2; ++i) fa[i] = i;
        for(register int i = 1; i <= m; ++i) {
            int x = a[i].x, y = a[i].y;
            if(get_fa(x) == get_fa(y)) continue;
            merge(x, y, a[i].z);
        }
        dep[tot] = 1, dfs0(tot);
        int Q = read(), K = read(), S = read(), ans = 0;
        while(Q--) {
            int v = read(), p = read();
            v = (v + K * ans - 1) % n + 1;
            p = (p + K * ans) % (S + 1);
            ans = query(v, p);
            printf("%d
", ans);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/longdie/p/kruskal.html