609E - Minimum spanning tree for each edge

给出一个带权图,对每个边求经过这条边的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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值