zoukankan      html  css  js  c++  java
  • Kruskal重构树

    Kruskal重构树

    前置知识:Kruskal算法, LCA

    最近刚学,如有错误欢迎提出。

    对图建Kruskal重构树的主要作用:求原图中两个点所在路径上的最长边的最小值或最短边的最大值。

    如何建Kruskal重构树

    在Kruskal的基础上,当两个点x, y 不再一个并查集中,那么我们就再引入一个新的点,这个点的权值是x,y两点再原图中的边的权值。最后把两个并查集的根节点指向新的点。

    (下图中右边是左边图建立的Kruskal重构树)

    • 首先是连4和6两个点,引入一个新点7,连边4和7以及6和7,其权值是2
    • 然后连3和4这一条边,引入一个新点8,连3和4所在的并查集的根节点7,权值为3.
    • 后面和前面一样,和kruskal算法过程差不多。

    为什么能算出来呢?

    首先如果要求两点路径最长边的最小值。我们需要按照边权从小到大去建kruskal重构树,注意所有新点表示的其实是一条边,这时只有x, y两点lca这个点连边x和y才能联通,那么才能有一条路径使x,y相连,由于从小到大连边,那么最后连的lca表示的这条边一定是路径中的最大边,

    又因为后面连的边都比lca表示的边大,后面出现的x,y之间的边的最大值一定比lca这条边大,所以lca点表示的边是x,y之间路径的最大边的最小的那一条。


    求两点之间路径最小边的最大值思路一样,不过是从大到小建kruskal重构树。

    代码实现:

    代码其实很清楚,看别的博客把lca和建树写在一起了,这样虽然很好写,但是我感觉会让新学习的人看到各种命名的数组很懵。所以我分开了。

    建树:

    //add(u,v)是建链式前向星图
    //Find(x)是找x并查集根节点,root[x]表示x所在并查集根节点
    //e[i]存原图,排序后建kruskal重构树
    //tot表示新点,初值为n
    for(int i = 1; i <= m; ++ i){
       	int x = e[i].u, y = e[i].v; ll z = e[i].w;
       	int tx = Find(x), ty = Find(y);
      	if(tx == ty) continue;
       	root[tx] = root[ty] = ++ tot;	//把两个并查集根指向新的点
       	val[tot] = z;					//新点的权值是新连边的边权
       	add(tot, tx); add(tot, ty);	//建重构树,把新点和两个并查集根连边
    }
    

    是的,上面就建好kurskal重构树了。和求kruskal算法过程简直很像。

    下面是求LCA,用自己板子就行了。

    //f[i][j]表示从i点向上2^j步的点
    void bfs(){
        q.push(tot);
        dep[tot] = 1;
        while(!q.empty()){
            int u = q.front();
            q.pop();
            for(int i = head[u]; i != -1; i = nxt[i]){
                int v = to[i];
                if(dep[v]) continue;
                dep[v] = dep[u] + 1;
                f[v][0] = u;
                for(int j = 1; j <= t; ++ j)
                    f[v][j] = f[f[v][j - 1]][j - 1];
                q.push(v);
            }
        }
    }
    
    int Lca(int x, int y){
        if(dep[x] > dep[y]) swap(x, y);
        for(int i = t; i >= 0; -- i)
            if(dep[f[y][i]] >= dep[x]) y = f[y][i];
        if(x == y) return x;
        for(int i = t; i >= 0; -- i)
            if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    

    查询两点x,y之间路径最大边的最小值。

    while(Q --){
    	int x, y; scanf("%d%d",&x,&y);
    	int lca = Lca(x, y);
    	printf("%lld
    ",val[lca]);
    }
    

    完整代码:(bzoj 3732 Network)

    由于bzoj好像没了,所以代码没交过
    更新:交到darkbzoj上过了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<queue>
    #include<vector>
    #include<string>
    #include<bitset>
    #include<fstream>
    using namespace std;
    #define rep(i, a, n) for(int i = a; i <= n; ++ i);
    #define per(i, a, n) for(int i = n; i >= a; -- i);
    typedef long long ll;
    typedef pair<ll,int> PII;
    const int N = 2e6 + 105;
    const int mod = 1e9 + 7;
    const double Pi = acos(- 1.0);
    const ll INF = 1e18; 
    const int G = 3, Gi = 332748118;
    ll qpow(ll a, ll b) { ll res = 1; while(b){ if(b & 1) res = (res * a) % mod; a = (a * a) % mod; b >>= 1;} return res; }
    ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
    // bool cmp(int a, int b){return a > b;}
    //
    
    int n, m, Q, t;
    int head[N], cnt = 0, tot;
    int to[N << 1], nxt[N << 1]; ll c[N << 1];
    int root[N << 1];
    int f[N][20];
    int dep[N];
    queue<int> q;
    ll val[N];
    
    void add(int u, int v){
        to[cnt] = v, nxt[cnt] = head[u], head[u] = cnt ++;
        to[cnt] = u, nxt[cnt] = head[v], head[v] = cnt ++;
    }
    
    int Find(int x){
        return x == root[x] ? x : root[x] = Find(root[x]);
    }
    
    struct node{
        int u, v; ll w;
        bool operator < (const node &a) const {return w < a.w;}
    }e[N << 1];
    
    void bfs(){
        q.push(tot);
        dep[tot] = 1;
        while(!q.empty()){
            int u = q.front();
            q.pop();
            for(int i = head[u]; i != -1; i = nxt[i]){
                int v = to[i];
                if(dep[v]) continue;
                dep[v] = dep[u] + 1;
                f[v][0] = u;
                for(int j = 1; j <= t; ++ j)
                    f[v][j] = f[f[v][j - 1]][j - 1];
                q.push(v);
            }
        }
    }
    
    int Lca(int x, int y){
        if(dep[x] > dep[y]) swap(x, y);
        for(int i = t; i >= 0; -- i)
            if(dep[f[y][i]] >= dep[x]) y = f[y][i];
        if(x == y) return x;
        for(int i = t; i >= 0; -- i)
            if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&Q);
        cnt = 0; tot=  n;
        for(int i = 0; i <= n * 2 + 1; ++ i){
            root[i] = i;
            head[i] = -1;
        }
        for(int i = 1; i <= m; ++ i){
            int x, y; ll z; scanf("%d%d%lld",&x,&y,&z);
            e[i] = (node){x, y, z};
        }
        sort(e + 1, e + m + 1);
        for(int i = 1; i <= m; ++ i){
            int x = e[i].u, y = e[i].v; ll z = e[i].w;
            int tx = Find(x), ty = Find(y);
            if(tx == ty) continue;
            root[tx] = root[ty] = ++ tot;
            val[tot] = z;
            add(tot, tx); add(tot, ty);
        }
        
        t = (int)(log(n) / log(2)) + 1;
        bfs();
        while(Q --){
            int x, y; scanf("%d%d",&x,&y);
            int lca = Lca(x, y);
            printf("%lld
    ",val[lca]);
        }
        return 0;
    }
    
    
  • 相关阅读:
    bzoj 3993: [SDOI2015]星际战争
    bzoj 4066: 简单题
    bzoj 3611: [Heoi2014]大工程
    bzoj 3530: [Sdoi2014]数数
    bzoj 3529: [Sdoi2014]数表
    bzoj 3504: [Cqoi2014]危桥
    bzoj 3489: A simple rmq problem
    bzoj 3211: 花神游历各国
    bzoj 3196: Tyvj 1730 二逼平衡树
    bzoj 3172: [Tjoi2013]单词
  • 原文地址:https://www.cnblogs.com/A-sc/p/13740656.html
Copyright © 2011-2022 走看看