Kruskal重构树

12 篇文章 0 订阅
3 篇文章 0 订阅

前置芝士

1.认识什么是图

2.认识什么是树

3.了解kruskal

4.倍增

定义

我乱理解的

kruskal重构树是在基于kruskal思想上进行重构(即加上虚点)得到的一棵二叉树,且符合二叉堆性质。

这些虚点其实就是生成树上两个点的边权变为了点权

方法

这里以最小生成树为例子,先用kruskal的方法得到一些边,在加边时用并查集维护,即先找到它们的祖先,然后新建一个点,向它们的祖先连边

现在有以下性质:

1.kruskal总共有2n-1个点,且实点(感性理解,非虚点)是一定在叶子上的,因此可以得出对于两个点的LCA的点权一定是这张图中最短路径中最长的边权值

2.每个非叶子点都代表一条边

根据此,可以求出一些最..最..问题。。。

例题

1.货车运输

A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。

现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

 

当学会kruskal重构树后,可以很容易发现其实这就是要在二叉树上做倍增即可

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 2e4 + 3;
const int MAXM = 5e4 + 3;
const int M = 1e7 + 3;
inline char GetChar(){
    static char buf[10001],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,10001,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int &n){
    short f=1;
    long long x=0;
    char c=GetChar();
    while(isdigit(c)==false){
        if(c=='-'){
            f=-1;
		}
        c=GetChar();
    }
    while(isdigit(c)==true){
    	x=((x<<3)+(x<<1)+(c^48));
        c=GetChar();
    }
    n=x*f;
}
int n , m , q , son[MAXN][2] , f[MAXN][23];
struct edge{
    int x , y , w;
    friend bool operator < ( edge a , edge b ){
        return a.w > b.w;
    }
}G[MAXM];
int fa[MAXN];
int findSet( int x ){
    if( x != fa[x] ) fa[x] = findSet( fa[x] );
    return fa[x];
}
int dep[MAXN];
void dfs( int x ){
    if( !son[x][0] ) return ;
    for( int i = 0 ; i <= 1 ; i ++ ){
        int v = son[x][i];
        dep[v] = dep[x] + 1;
        dfs( v );
    }
}
void jump( int &x , int mu ){
    for( int i = 20; i >= 0 ; i-- ){
        if( dep[f[x][i]] >= mu ) x = f[x][i];
    }
}
int LCA( int x , int y ){
    if( dep[x] < dep[y] ) swap( x , y );
    if( dep[x] != dep[y] ) jump( x , dep[y] );
    for( int i = 20 ; i>= 0; i -- ){
        if(f[x][i] != f[y][i] )
            x = f[x][i] , y = f[y][i];
    }
    return f[x][0];
}
ll c[MAXN];
int main(){
    scanf( "%d%d" , &n , &m );
    for( int i = 1 ; i <= m ; i ++ ){
        int x , y , z;scanf( "%d%d%d" , &x , &y , &z );
        G[i].x = x , G[i].y = y , G[i].w = z;
    }
    sort( G + 1 , G + m + 1 );
    int k = 0 ,ncnt = n;
    for( int i = 1 ; i <= n * 2 - 1; i ++ ) fa[i] = i;
    for( int i = 1 ; i <= m ;i ++ ){
        int x = G[i].x , y = G[i].y , w = G[i].w;
        int u = findSet( x ) , v = findSet( y );
        if( u == v ) continue;
        fa[u] = fa[v] = ++ncnt;
        son[ncnt][0] = u , son[ncnt][1] = v;
        f[u][0] = f[v][0] = ncnt;
        c[ncnt-n] = w;
        k ++ ;
        if( k == n - 1 ) break;
    }
    dep[ncnt] = 1;
    dfs( ncnt );
    for( int j = 1;  j <= 20 ; j ++ )
        for( int i = 1; i <= ncnt ; i ++ )
            f[i][j] = f[f[i][j-1]][j-1];
    scanf( "%d" , &q );
    while( q -- ){
        int x , y;scanf( "%d%d" , &x , &y );
        if( findSet( x) != findSet( y ) ) {
            printf( "-1\n" );
            continue;
        }
        int lca = LCA( x , y );
        printf( "%d\n" , c[lca-n] );
    }
    return 0;
}

例题2:归程

当发现这道题是一道kruskal重构树的时候,就已经完成一半了

毕竟是一道签到题,难度也不是太大

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 2e5 + 3;
const int MAXM = 4e5 + 3;
int n , m , head[MAXN] , cnt = 1;
ll dis[MAXN];
struct node{
    int x , y , l , a;
    friend bool operator < ( node a , node b ){
        return a.a > b.a;
    }
}E[MAXM];
bool flag[MAXM] , vis[MAXN];
struct edge{
    int nex ,to , l;
}G[MAXM<<1];
int f[MAXN<<1][24] , fa[MAXN<<1] , minn[MAXN<<1][24] , son[MAXN<<1][2] , dep[MAXN<<1];
ll dminn[MAXN<<1];
int findSet( int x ){
    if( x != fa[x] ) fa[x] = findSet( fa[x] );
    return fa[x];
}
struct node1{
    int x;ll dis;
    node1(){}
    node1( int X , ll D ){
        x = X , dis = D;
    }
    friend bool operator < ( node1 a , node1 b ){
        return a.dis > b.dis;
    }
};
priority_queue<node1>q;
void add_edge( int x , int y , int l ){
    G[cnt].to = y , G[cnt].l = l , G[cnt].nex = head[x];
    head[x] = cnt ++;
}
void dfs( int x ){
    if( !son[x][0] ){
        dminn[x] = dis[x];
        return ;
    }
    for( int i = 0 ; i <= 1 ; i ++ ){
        int v = son[x][i];
        dep[v] = dep[x] + 1;
        dfs( v );
    }
    dminn[x] = min( dminn[son[x][0]] , dminn[son[x][1]] );
}
int main(){
	//freopen( "2.in" , "r" , stdin );
	//freopen( "2.out" , "w" , stdout );
    int T;
    scanf( "%d" , &T );
    while( T -- ){
        memset( flag , 0 , sizeof( flag ) );
        memset( head , 0 , sizeof( head ) );
        memset( vis , 0 , sizeof( vis) );
        memset( son , 0 , sizeof( son ) );
        memset( dep , 0 , sizeof( dep ) );
        memset( f , 0 , sizeof( f ) );
        memset( minn , 0 , sizeof( minn ) );cnt = 1;
        scanf( "%d%d" , &n , &m );
        for( int i = 1 ; i <= m ; i ++ ){
            scanf( "%d%d%d%d" , &E[i].x , &E[i].y , &E[i].l , &E[i].a );
            add_edge( E[i].x , E[i].y , E[i].l );
            add_edge( E[i].y , E[i].x , E[i].l );
        }
        sort( E + 1 , E + m + 1 );
        for( int i = 1 ; i <= n * 2 ; i ++ ) fa[i] = i;
        for( int i = 1 ; i <= n ; i ++ ) dis[i] = 0x7f7f7f7f7f;
        dis[1] = 0;
        q.push( node1( 1 , 0 ) );
        while( !q.empty() ){
            node1 tx = q.top();q.pop();
            int x = tx.x;
            if( vis[x] ) continue;
            vis[x] = 1;
            for( int i = head[x] ; i ; i = G[i].nex ){
                int v = G[i].to , w = G[i].l;
                if( dis[v] > dis[x] + w ){
                    dis[v] = dis[x] + w;
                    q.push( node1( v , dis[v] ) );
                }
            }
        }
        int ncnt = n , k = 0;
        for( int i = 1 ; i <= m ; i ++ ){
            int x = E[i].x,  y = E[i].y , w = E[i].a;
            int u = findSet( x ) , v = findSet( y );
            if( u == v ) continue;
            fa[u] = fa[v] = ++ncnt;
            son[ncnt][0] = u , son[ncnt][1] = v;
            k ++ ;
            f[u][0] = f[v][0] = ncnt;minn[u][0] = minn[v][0] = w;
            if( k == n - 1) break;
        }
        //for( int i = 1 ; i < ncnt ; i ++ ) 
        //	printf( "%d\n" , minn[i][0] );
        dep[ncnt] = 1;
        dfs( ncnt );
        for( int j = 1 ; j <= 22 ; j ++ ){
            for( int i = 1 ; i <= ncnt ; i ++ ){
                f[i][j] = f[f[i][j-1]][j-1];
                minn[i][j] = min( minn[f[i][j-1]][j-1] ,minn[i][j-1] );
            }
        }
        ll Q , K , S, lastans = 0;
        scanf( "%lld%lld%lld" , &Q , &K , &S );
        while( Q -- ){
            ll v0 , p0;scanf( "%lld%lld" , &v0 , &p0 );
            int v = ( v0 + K * lastans - 1 ) % n + 1;
            int p = ( p0 + K * lastans ) % ( S + 1 );
            //printf( "%d\n" , p );
            for( int i = 22 ; i >= 0 ; i -- ){
                if( dep[f[v][i]] && minn[v][i] > p )
                    v = f[v][i];
            }
            printf( "%lld\n" , dminn[v] );
            lastans = dminn[v];
        }
    }
}

例题3:意念的交流

点击此处查看详情

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值