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\n",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\n",val[lca]);
    }
    return 0;
}

posted @ 2020-09-27 17:17  A_sc  阅读(170)  评论(0编辑  收藏  举报