kruscal重构树

发布于:2025-07-29 ⋅ 阅读:(15) ⋅ 点赞:(0)

构造:

  • 把最小生成树上的边从小到大取出来。

这一步在实现上相当于边做 Kruskal 边构造重构树。

利用这个性质,我们可以找到到点 x 的简单路径上的边最大权值的最小值 ≤val 的所有点 y。可以发现都在 Kruskal 重构树上的某一棵子树内,且恰好为该子树的所有叶节点。

  • 设这条边的为 x 与 y 连边,那么我们新建节点 now,将 now 连向 x,y,将 now 的点权设为这条边的边权,x 与 y 之间不连边。注意这里向 x 连边实际上指的是向 x 所在子树的根节点连边。

  • 建完最小生成树之后,重构树也就建好了。

  • 这是一张无向带权图。

  • 这是这张图的最小生成树。

    我们将每条边从小到大建立 Kruskal 重构树。

  • 这样我们就建好了这个图的 Kruskal 重构树。

  • //kruscal重构树
        int idx = n;
        for(auto &[x,y,w]:edge){
            int fx = root(x);
            int fy = root(y);
            if(fx != fy){
                idx ++ ;
                pre[fx] = pre[fy] = idx;
                val[idx] = w;
                g[idx].push_back(fx);
                g[idx].push_back(fy);
                if(idx == 2 * n - 1)break;
            }
        }

    性质:

  • 是一棵二叉树。

  • 如果是按最小生成树建立的话是一个大根堆。

  • 强大性质:原图中两个点间所有路径上的边最大权值的最小值 = 最小生成树上两点简单路径的边最大权值 = Kruskal 重构树上两点 LCA 的点权。

 例题:

1706E - Qpwoeirut and Vertices

我们将这条边的编号定义为它的边权。通过 Kruskal 生成树算法,连边时断开这条边,而是新建一个点,让这个新点来连接两点,新点的点权为原来连边的权值。这就是 Kruskal 重构树。

得到 Kruskal 重构树后,为了 u 和 v 连通,连接的边的最大值的最小值,即为 u 和 v 的最近公共祖先的点权。通过倍增法求出最近公共祖先即可。

对于所有的 i(1≤i<n),我们可以先求出使 i 和 i+1 连通的代价(设为 ansi​)。然后,建造线段树,预处理出区间最大值。

最后,询问 l,r 本质上就是使 [l,1+1],[l+1,l+2],…[r−1,r] 都连通,那么答案就是 maxi=lr−1​ansi​。利用线段树求出即可。

代码:

#pragma GCC optimize(2,"Ofast","inline")
#include <bits/stdc++.h>

#define int long long
#define double long double
#define ull unsigned long long
#define fi first 
#define se second
#define ls(p) p<<1
#define rs(p) (p<<1)|1
#define YES cout<<"YES\n"
#define NO cout<<"NO\n"
#define Yes cout<<"Yes\n"
#define No cout<<"No\n"
#define sp(x) fixed << setprecision(x)
#define all(v) v.begin(),v.end()

using namespace std;
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ull rnd() { return (unsigned long long)rng(); }

const int inf = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 5;
int n,m,q;
vector<int>g[N];
struct node{
    int x,y,w;
    bool operator<(const node &u)const{
        return w < u.w;
    }
};
vector<node>edge;
int pre[N];
int val[N];//点权
int fa[N][24],dep[N];
int tr[N];
int b[N];

int root(int x){
    return pre[x] = (pre[x] == x) ? x : root(pre[x]);
}

void init(){
    edge.clear();
    for(int i=1;i<=n*2LL;i++){
        dep[i] = 0;
        g[i].clear();
        pre[i] = i;
        for(int j=1;j<=20;j++)fa[i][j] = 0;
    }
    return;
}

void dfs(int x,int p){//p为父亲
	dep[x]=dep[p]+1;
	fa[x][0]=p;
	for(int i=1;i<=20;i++){
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	for(const auto &y:g[x]){
		if(y==p)continue;
		dfs(y,x);
	}
}

int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	
	for(int i=20;i>=0;i--){
		if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
	}
	
	if(x==y)return x;
	
	for(int i=20;i>=0;i--){
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}

void push_up(int p){
    tr[p] = max(tr[ls(p)] , tr[rs(p)]);
    return;
}

void build(int p,int l,int r){
    if(l == r){
        tr[p] = b[l];
        return;
    }
    int mid = l+r>>1;
    build(ls(p) , l , mid);
    build(rs(p) , mid + 1 , r);
    push_up(p);
    return;
}

int query(int p,int l,int r,int nl,int nr){
    if(nl <= l && r <= nr){
        return tr[p];
    }

    int mid = l+r>>1;
    int res = 0;
    if(nl <= mid)res = max(res , query(ls(p) , l , mid , nl , nr));
    if(nr > mid)res = max(res , query(rs(p) , mid + 1 , r , nl , nr));
    return res;
}

void solve(){
    cin>>n>>m>>q;
    init();

    for(int i=1;i<=m;i++){
        int x,y;cin>>x>>y;
        int w = i;
        edge.push_back({x , y , w});
    }
    
    sort(all(edge));

    //kruscal重构树
    int idx = n;
    for(auto &[x,y,w]:edge){
        int fx = root(x);
        int fy = root(y);
        if(fx != fy){
            idx ++ ;
            pre[fx] = pre[fy] = idx;
            val[idx] = w;
            g[idx].push_back(fx);
            g[idx].push_back(fy);
            if(idx == 2 * n - 1)break;
        }
    }

    dfs(idx , 0);

    for(int i=1;i<n;i++){
        int p = lca(i , i + 1);
        b[i] = val[p];
    }

    build(1 , 1 , n - 1);

    while(q--){
        int l,r;cin>>l>>r;
        if(l == r){
            cout<<0<<" ";
            continue;
        }
        cout<<query(1 , 1 , n - 1 , l , r - 1)<<" ";
    }
    cout<<"\n";

    return;

}

signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    time_t Start = clock();
    int T = 1;
    cin >> T;
    while (T -- ){
        solve();
    }
    time_t End = clock(); 
    time_t Run_time = End - Start;
	//cout<<"\n"<<Run_time<<"ms"<<"\n"; 
    return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到