609E - Minimum spanning tree for each edge

本文介绍了一种算法,用于解决给定带权图中,针对每条边求解其在最小生成树(MST)中的权重变化问题。首先通过一次遍历找出MST,接着对MST中的边直接返回全局最小权值;对于非MST边,则查找替换路径上最大权值边后的最小生成树权值。

给出一个带权图,对每个边求经过这条边的MST


先求一遍MST自然是极好的,然后对于在MST上的边,自然就是全局MST了

对于不在MST上的边,找到这个边两个端点在MST上的路径上最大权的边并去掉,然后加上询问的这条边的权就好了

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

const int maxn = 212345,maxm = 212345,max_log = 18;
const int ROOT = 1;
vector<pair<int,LL> >edge[maxn];
void Link(int st,int ed,LL v){
    edge[st].push_back(make_pair(ed,v));
}

int deep[maxn],fa[maxn][max_log];
int nofa[maxn];

struct Info{
    LL x;
    Info(LL x = 0):x(x){};
};

Info operator + (const Info a,const Info b){
    return Info(max(a.x,b.x));
}

Info info[maxn][max_log];

void dfs(int st,int Fa,int Deep=1){
    for(int i=1;i<max_log;i++)
        fa[st][i] = -1,info[st][i] = Info();
    fa[st][0] = Fa,deep[st] = Deep;
    for(auto x : edge[st]){
        if(x.first == Fa) continue;
        dfs(x.first,st,Deep+1);
        info[x.first][0] = Info(x.second);
    }
}
void init(int n){
    memset(fa,-1,sizeof(fa));
    dfs(ROOT,-1);
    for (int j = 1;j < max_log;j++){
        for (int i = 1;i <= n;i++){
            if (fa[i][j-1] != -1){
                fa[i][j] = fa[fa[i][j-1]][j-1];
                info[i][j] = info[i][j-1] + info[fa[i][j-1]][j-1];
            }
        }
    }
}

pair<int,Info> lca(int x,int y){
    Info ix,iy;
    if (deep[x] < deep[y]) swap(x,y);
    for (int i = max_log-1;i >= 0;i--){
        if (deep[fa[x][i]] >= deep[y]){
            ix = ix + info[x][i];
            x = fa[x][i];
        }
    }
    if (x == y) return make_pair(x,ix);
    for (int i = max_log-1;i >= 0;i--){
        if (fa[x][i] != fa[y][i]){
            ix = ix + info[x][i]; x = fa[x][i]; 
            iy = iy + info[y][i]; y = fa[y][i]; 
        }
    }
    return make_pair(fa[x][0], ix/*.rev()*/+info[x][0] + info[y][0] + iy);
}

struct Edge{
    int x,y;
    LL v,i;
    void init(int id){
        scanf("%d %d %I64d",&x,&y,&v);
        i = id;
    }
}edg[maxm];

struct Dsu{
    int arr[maxn];
    void init(int n){for(int i=0;i<=n;i++) arr[i] = i;}
    int fnd(int x){return x == arr[x] ? x : arr[x] = fnd(arr[x]);}
    bool join(int x,int y){
        x = fnd(x),y = fnd(y);
        if(x == y) return false;
        arr[x] = y;
        return true;
    }
}dsu;

int main(){
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=0;i<m;i++){
        edg[i].init(i);
    }
    sort(edg,edg+m,[](Edge a,Edge b){return a.v < b.v;});
    dsu.init(n);
    LL all = 0;
    for(int i=0;i<m;i++){
        if(dsu.join(edg[i].x,edg[i].y)){
            all += edg[i].v;
            Link(edg[i].x,edg[i].y,edg[i].v);
            Link(edg[i].y,edg[i].x,edg[i].v);
        }
    }
    init(n);
    sort(edg,edg+m,[](Edge a,Edge b){return a.i < b.i;});
    for(int i=0;i<m;i++){
        printf("%I64d\n",all + edg[i].v - lca(edg[i].x,edg[i].y).second.x);
    }
    return 0;
}
### 检查最小生成树是否唯一的方法 要确定一个连通无向图的最小生成树(MST)是否唯一,可以通过 Kruskal 算法或 Prim 算法结合并查集(Union-Find)数据结构实现。以下是具体方法: #### 1. 使用 Kruskal 算法检查 MST 的唯一性 Kruskal 算法通过按权重排序边并选择不形成环的边来构建 MST。为了检查 MST 是否唯一,可以扩展 Kruskal 算法如下: - 首先对所有边按权重升序排序。 - 在构建 MST 的过程中,对于每条权重为 \( w \) 的边,记录有多少条这样的边被加入到 MST 中。 - 如果存在多条权重相同的边可以选择,且这些边的选择会影响最终的 MST,则说明 MST 不唯一。 具体实现中,可以在 Union-Find 数据结构中跟踪每次合并操作,并检查是否有多种方式选择边[^1]。 ```python def is_mst_unique(edges, n): # Sort edges by weight edges.sort(key=lambda x: x[2]) # (u, v, weight) parent = list(range(n)) rank = [0] * n def find(u): while parent[u] != u: parent[u] = parent[parent[u]] u = parent[u] return u def union(u, v): root_u = find(u) root_v = find(v) if root_u == root_v: return False if rank[root_u] > rank[root_v]: parent[root_v] = root_u elif rank[root_u] < rank[root_v]: parent[root_u] = root_v else: parent[root_v] = root_u rank[root_u] += 1 return True mst_weight = 0 count = 0 unique = True i = 0 while count < n - 1 and i < len(edges): u, v, weight = edges[i] j = i # Count how many edges have the same weight while j < len(edges) and edges[j][2] == weight: j += 1 same_weight_edges = edges[i:j] valid_edges = [] for edge in same_weight_edges: u, v, _ = edge if union(u, v): valid_edges.append(edge) if len(valid_edges) > 1: unique = False mst_weight += sum(edge[2] for edge in valid_edges) count += len(valid_edges) i = j return unique if count == n - 1 else False ``` #### 2. 使用 Prim 算法检查 MST 的唯一性 Prim 算法从一个顶点开始逐步扩展 MST。类似地,可以通过以下方法检查 MST 是否唯一: - 在每个步骤中,检查是否存在多条权重相同的边可以加入当前的 MST。 - 如果存在多条这样的边,则说明 MST 不唯一。 在实现中,可以使用优先队列(如堆)来选择最小权重的边,并记录每次选择时的候选边数量[^1]。 ```python import heapq def is_mst_unique_prim(graph, n): visited = [False] * n pq = [(0, 0)] # (weight, vertex) mst_weight = 0 unique = True while pq: weight, u = heapq.heappop(pq) if visited[u]: continue visited[u] = True mst_weight += weight candidates = [] for v, w in graph[u]: if not visited[v]: candidates.append((w, v)) candidates.sort() if len(candidates) > 1 and candidates[0][0] == candidates[1][0]: unique = False for w, v in candidates: heapq.heappush(pq, (w, v)) return unique if all(visited) else False ``` ### 总结 通过扩展 Kruskal 或 Prim 算法,可以在构建 MST 的过程中检查是否存在多条权重相同的边可以被选择。如果存在这样的情况,则说明 MST 不唯一。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值