kruskal 重构树

实现

  1. 将无向图的边按边权排序。
  2. 枚举排序后的边 \(e\),若 \(e\) 的两个端点 \(x, y\) 不连通,那么新建节点 \(p\),点 \(p\) 连接 \(x, y\) 所在连通块的根,将 \(p\) 的点权设为 \(e\) 的边权。
  3. 枚举结束后,得到的一棵树成为 kruskal 重构树。

性质

  1. 原图中的 \(n\) 个点都是树上的叶子节点。
  2. 树上共 \(2 \times n - 1\) 个点。
  3. 除开叶子,具有堆的性质。
  4. kruskal 重构树任意子树内的节点,在原图中可以不依赖子树外的点连通。

例题

P1967 [NOIP 2013 提高组] 货车运输

建出 kruskal 重构树,跳 LCA 即可。

代码:

#include <bits/stdc++.h>
#define int long long
#define pb emplace_back
using namespace std;

const int N = 1e5 + 5;
const int M = 5e4 + 5;
const int L = 31;
int n, m, Q, u, v, w, tot, lg[N << 1], val[N << 1], dep[N << 1], fat[N][L];
struct Edge {
    int u, v, w;
} e[M];
vector<int> g[N << 1];

namespace USF {
    int fa[N << 1];

    inline void init() {
        for(int i = 1 ; i < N << 1 ; ++ i)
            fa[i] = i;

        return ;
    }

    inline int find(int x) {
        if(x != fa[x]) fa[x] = find(fa[x]);

        return fa[x];
    }

    inline void merge(int x, int y) {
        x = find(x), y = find(y);

        if(x == y) return ;

        fa[y] = x;

        return ;
    }
}

using namespace USF;

inline void build_kruskal() {
    init();

    sort(e + 1, e + 1 + m, [&](Edge a, Edge b) {
        return a.w > b.w;
    });

    tot = n;

    for(int i = 1 ; i <= m ; ++ i) {
        u = e[i].u, v = e[i].v, w = e[i].w;

        if(find(u) == find(v)) continue;

        u=find(u),v = find(v);

        g[u].pb(++ tot), g[tot].pb(u);
        g[v].pb(tot), g[tot].pb(v);
        //cout<<tot<<" "<<u<<"\n";
        //cout<<tot<<" "<<v<<"\n";
        merge(tot, u), merge(tot, v);
        val[tot] = w;
    }
    //cout<<tot+1<<" "<<find(6)<<" "<<find(tot+1)<<"&\n";
    for(int i = 1 ; i <= tot ; ++ i)
        if(find(tot + 1) != find(i)) {
            g[tot + 1].pb(find(i)), g[find(i)].pb(tot + 1);
            //cout<<tot+1<<" "<<i<<"*\n";
            merge(tot + 1, i);
        }
}

inline void dfs(int x, int last) {
    for(auto u : g[x])
        if(u != last) {
            fat[u][0] = x;
            //cout<<u<<" "<<x<<" "<<fat[u][0]<<"\n";
            dep[u] = dep[x] + 1;

            dfs(u, x);
        }

    return ;
}

inline void build_lca() {
    for(int i = 1 ; 1 << i <= tot + 1 ; ++ i)
        for(int j = 1 ; j <=  tot + 1 ; ++ j)
            fat[j][i] = fat[fat[j][i - 1]][i - 1];
    
    lg[1] = 0;
    for(int i = 2 ; i < N << 1 ; ++ i)
        lg[i] = lg[i >> 1] + 1;

    return ;
}

inline int LCA(int x, int y) {
    if(dep[x] < dep[y]) swap(x, y);

    int dis = dep[x] - dep[y];

    for(int i = lg[dis] ; ~ i ; -- i)
        if(dis - (1 << i) >= 0) dis -= (1 << i), x = fat[x][i];

    if(x == y) return x;

    for(int i = lg[dep[x]] ; ~ i ; -- i)
        if(fat[x][i] != fat[y][i]) x = fat[x][i], y = fat[y][i];

    return fat[x][0];
}

signed main() {
    ios_base :: sync_with_stdio(NULL);
    cin.tie(nullptr);
    cout.tie(nullptr);

    cin >> n >> m;
    for(int i = 1 ; i <= m ; ++ i) {
        cin >> u >> v >> w;
        e[i] = {u, v, w};
    }

    build_kruskal();
    dfs(tot + 1, -1);
    build_lca();

    cin >> Q;
    
    while(Q --) {
        cin >> u >> v;
        w = LCA(u, v);
        //cout<<w<<"*\n";
        if(w == tot + 1) cout << "-1\n";
        else cout << val[w] << '\n';
    }

    return 0;
}

P4197 Peaks

对于路径权值的限制,用 kruskal 重构树解决;对于可达第 \(k\) 小,按深度建可持久化线段树即可。

代码:

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;

const int N = 2e5 + 5;
const int M = 5e5 + 5;
const int L = log2(N) + 5;
int n, m, Q, u, v, w, k, tot, times, a[N], lg[N], to[N], sz[N], val[N], dfn[N], dep[N], leaves[N], fa[N][L];
struct Node {
    int u, v, w;
} e[M];
vector<int> g[N];

namespace UFS {
    int f[N];
    
    inline void init_set() {
        for(int i = 1 ; i < N ; ++ i)
            f[i] = i;

        return ;
    }

    inline int find(int x) {
        if(x != f[x]) f[x] = find(f[x]);

        return f[x];
    }

    inline void merge(int x, int y) {
        x = find(x), y = find(y);
        
        if(x == y) return ;

        f[y] = x;

        return ;
    }
}

using namespace UFS;

namespace SGT {
    #define mid ((L + R) >> 1)
    #define son p, L, R
    #define lson t[p].ls, L, mid
    #define rson t[p].rs, mid + 1, R

    int cnt, rt[N];
    struct sgt {
        int ls, rs, sum;
    } t[N << 7];

    inline void psup(int p) {
        t[p].sum = t[t[p].ls].sum + t[t[p].rs].sum;

        return ;
    }

    inline void add(int x, int k, int v, int &p, int L = 0, int R = 1e9) {
        p = ++ cnt;
        t[p] = t[v];
        if(L == R) {
            t[p].sum += k;

            return ;
        }

        if(x <= mid) add(x, k, t[v].ls, lson);
        else add(x, k, t[v].rs, rson);

        psup(p);
        // if(L==0&&R==1e9)
        // cout<<k<<" "<<v<<" "<<p<<" "<<t[v].sum<<" "<<t[p].sum<<'\n';
        return ;
    }

    inline int query(int lpos, int rpos, int k, int L = 0, int R = 1e9) {
        if(L == R) return L;

        int res = t[t[rpos].ls].sum - t[t[lpos].ls].sum;

        if(k <= res) return query(t[lpos].ls, t[rpos].ls, k, L, mid);
        else return query(t[lpos].rs, t[rpos].rs, k - res, mid + 1, R);
    }

    #undef mid
    #undef son
    #undef lson
    #undef rson
}

using namespace SGT;

inline void build_kruskal() {
    init_set();

    sort(e + 1, e + 1 + m, [&](Node a, Node b) {
        return a.w < b.w;
    });

    tot = n;

    for(int i = 1 ; i <= m ; ++ i) {
        u = find(e[i].u), v = find(e[i].v), w = e[i].w;

        if(u == v) continue;

        val[++ tot] = w;
        merge(tot, u), merge(tot, v);
        g[tot].pb(u),g[tot].pb(v);
    }

    return ;
}

inline void dfs(int x, int last) {
    // cerr << "vex:" << x << '\n';

    sz[x] = 1;
    dfn[x] = ++ times;
    to[dfn[x]] = x;
    leaves[x] = (g[x].size() == 0);

    for(auto u : g[x])
        if(u != last) {
            fa[u][0] = x;
            dep[u] = dep[x] + 1;

            dfs(u, x);

            sz[x] += sz[u];
            leaves[x] += leaves[u];
        }

    return ;
}

inline void init_jump() {
    for(int i = 2 ; i < N ; ++ i)
        lg[i] = lg[i >> 1] + 1;

    for(int i = 1 ; (1 << i) <= tot ; ++ i)
        for(int j = 1 ; j <= tot ; ++ j)
            fa[j][i] = fa[fa[j][i - 1]][i - 1];

    return ;
}

inline int jump(int x, int k) {
    for(int i = L-1 ; ~ i ; -- i)
        if(fa[x][i]!=0 && val[fa[x][i]] <= k) x = fa[x][i];

    return x;
}

signed main() {
    ios_base :: sync_with_stdio(NULL);
    cin.tie(nullptr);
    cout.tie(nullptr);

    fill(a + 1, a + N, 1e18);

    cin >> n >> m >> Q;
    for(int i = 1 ; i <= n ; ++ i)
        cin >> a[i];
    for(int i = 1 ; i <= m ; ++ i) {
        cin >> u >> v >> w;
        e[i] = {u, v, w};
    }

    build_kruskal();
    dfs(tot, -1);
    init_jump();
    
    for(int i = 1 ; i <= tot ; ++ i)
    {
        if(to[i]<=n)
        add(a[to[i]], 1, rt[i - 1], rt[i]);
        else
        add(a[to[i]], 0, rt[i-1], rt[i]);
    }
        
    
    // for(int i = 1 ; i <= tot ; ++ i)
    //     cerr << "leaves:" << leaves[i] << '\n';

    while(Q --) {
        cin >> u >> k >> w;

        // cerr << "vex:" << u << '\n';
        u = jump(u, k);
        // cerr << "addvex:" << u << '\n';
        // cout<<leaves[u]<<"*"<<'\n';
        // cerr<<leaves[u]-w+1<<"*"<<'\n';
        // cerr<<t[rt[dfn[u] - 1]].sum<<" "<<t[rt[dfn[u] + sz[u] - 1]].sum<<'\n';
        if(leaves[u] < w) cout << "-1\n";
        else cout << query(rt[dfn[u] - 1], rt[dfn[u] + sz[u] - 1], leaves[u]-w+1) << '\n';
    }
    
    return 0;
}
/*
10 11 4
1 1 1 1 1 1 1 1 1 1
1 4 4
2 5 3
9 8 2
7 8 10
7 1 4
6 7 1
6 4 8
2 1 5
10 8 10
3 4 7
3 4 6
1 5 2
1 5 6
1 5 8
8 9 2
*/

P4768 [NOI2018] 归程

同样是用 kruskal 重构树解决边权限制和强制在线。那么接下来只要考虑一件事:怎样在重构树上点权不超过 \(k\) 的点的子树里求到 \(1\) 号点的最短路。

在原图上跑最短路,由于 kruskal 重构树的单调性,我们只需要事先对于重构树做一遍 dp,而后倍增求答案即可。

代码:

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;

const int N = 1e6 + 5;
const int M = log2(N) + 5;
int T, n, m, Q, K, s, u, v, w1, w2, lst, lg[N], dis[N], val[N], dep[N], fa[N][M];
bool vis[N];
struct Vex {
    int v, w;
};
struct Node {
    int u, v, w;
} e[N];
vector<Vex> g[N];
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;

namespace kruskal {
    int tot, f[N], ls[N], rs[N];

    inline void init_set() {
        for(int i = 1 ; i <= n << 1 ; ++ i)
            f[i] = i;

        return ;
    }

    inline int find(int x) {
        if(x != f[x]) f[x] = find(f[x]);

        return f[x];
    }

    inline void merge(int x, int y) {
        x = find(x), y = find(y);

        if(x == y) return ;

        f[y] = x;

        return ;
    }

    inline void build_kruskal() {
        tot = n;

        sort(e + 1, e + 1 + m, [&](Node a, Node b) {
            return a.w > b.w;
        });

        for(int i = 1 ; i <= m ; ++ i) {
            u = find(e[i].u), v = find(e[i].v);

            if(u == v) continue;

            val[++ tot] = e[i].w;
            ls[tot] = u, rs[tot] = v;
            merge(tot, u), merge(tot, v);
            dis[tot] = min(dis[u], dis[v]);

            // cerr << "root:" << u << ' ' << tot << ' ' << find(u) << '\n';
        }

        return ;
    }
}

using namespace kruskal;

inline pair<int, int> get(int x, int y) {
    return {(x + K * lst - 1) % n + 1, (y + K * lst) % (s + 1)};
}

inline void clear() {
    lst = 0;

    for(int i = 1 ; i <= n ; ++ i)
        g[i].clear();

    for(int i = 1 ; i <= tot ; ++ i) {
        for(int j = 0 ; j <= lg[dep[i]] ; ++ j)
            fa[i][j] = 0;

        ls[i] = rs[i] = val[i] = dep[i] = 0;
    }

    return ;
}

inline void dijkstra() {
    fill(dis + 1, dis + 1 + n * 2, 1e18);
    fill(vis + 1, vis + 1 + n * 2, false);
    dis[1] = 0;
    q.push({0ll, 1ll});

    while(! q.empty()) {
        auto [_, u] = q.top();
        q.pop();

        if(vis[u]) continue;
        vis[u] = true;

        for(auto [x, w] : g[u])
            if(dis[u] + w < dis[x]) {
                dis[x] = dis[u] + w;
                q.push({dis[x], x});
            }
    }

    return ;
}

inline void dfs(int x) {
    // cerr << "vex:" << x << '\n';

    if(! x) return ;

    fa[ls[x]][0] = fa[rs[x]][0] = x;
    dep[ls[x]] = dep[rs[x]] = dep[x] + 1;

    dfs(ls[x]), dfs(rs[x]);

    return ;
}

inline void init_jump() {
    for(int i = 2 ; i <= tot ; ++ i)
        lg[i] = lg[i >> 1] + 1;

    for(int i = 1 ; (1 << i) <= tot ; ++ i)
        for(int j = 1 ; j <= tot ; ++ j)
            fa[j][i] = fa[fa[j][i - 1]][i - 1];

    return ;
}

inline int jump(int x, int k) {
    if(val[fa[x][0]] < k) return x;

    for(int i = lg[dep[x]] ; ~ i ; -- i)
        if(dep[x] >= (1 << i) && val[fa[x][i]] > k) x = fa[x][i];

    return x;
}

signed main() {
    // freopen("xcy.in", "r", stdin);
    // freopen("xcy.out", "w", stdout);

    ios_base :: sync_with_stdio(NULL);
    cin.tie(nullptr);
    cout.tie(nullptr);

    cin >> T;

    while(T --) {
        clear();

        cin >> n >> m;
        for(int i = 1 ; i <= m ; ++ i) {
            cin >> u >> v >> w1 >> w2;

            e[i] = {u, v, w2};
            g[u].pb({v, w1}), g[v].pb({u, w1});
        }
        cin >> Q >> K >> s;

        dijkstra();
        init_set();
        build_kruskal();
        dfs(tot);
        init_jump();

        // for(int i = 1 ; i <= tot ; ++ i)
        //     cerr << "sons:" << ls[i] << ' ' << rs[i] << '\n';

        // for(int i = 1 ; i <= tot ; ++ i)
        //     cout << "dis:" << dis[i] << '\n';

        while(Q --) {
            cin >> u >> w1;

            auto [U, W] = get(u, w1);
            U = jump(U, W);

            cout << (lst = dis[U]) << '\n', lst = dis[U];
        }

        // cerr << val[tot] << '\n';
    }

    return 0;
}

/*
1
4 3
1 2 50 1
2 3 100 2
3 4 50 1
5 0 2
3 0
2 1
4 1
3 1
3 2
*/

P10054 [CCO 2022] Phone Plans

P5168 xtq玩魔塔

P9984 [USACO23DEC] A Graph Problem P

posted @ 2025-07-26 20:21  endswitch  阅读(65)  评论(0)    收藏  举报