Minimum spanning tree for each edge CodeForces - 609E(ST算法+树链剖分(或倍增LCA)+最小生成树)

本文介绍了一种算法思路,即如何在一个给定的无向带权图中,对于每条边找到包含该边的最小生成树(MST)的最小可能权值。通过使用并查集求解最小生成树,结合倍增LCA算法处理特定边的插入及环路中的最大权重边的删除,最终输出所有边对应的最小生成树权值。

Connected undirected weighted graph without self-loops and multiple edges is given. Graph contains n vertices and m edges.

For each edge (u, v) find the minimal possible weight of the spanning tree that contains the edge (u, v).

The weight of the spanning tree is the sum of weights of all edges included in spanning tree.

Input
First line contains two integers n and m (1 ≤ n ≤ 2·105, n - 1 ≤ m ≤ 2·105) — the number of vertices and edges in graph.

Each of the next m lines contains three integers ui, vi, wi (1 ≤ ui, vi ≤ n, ui ≠ vi, 1 ≤ wi ≤ 109) — the endpoints of the i-th edge and its weight.

Output
Print m lines. i-th line should contain the minimal possible weight of the spanning tree that contains i-th edge.

The edges are numbered from 1 to m in order of their appearing in input.

Example
Input
5 7
1 2 3
1 3 1
1 4 5
2 3 2
2 5 3
3 4 2
4 5 4
Output
9
8
11
8
8
8
9
思路:首先我们通过并查集来求出最小生成树,并求出树的权值和sum。现在枚举边,假设边就在这个树上,那么sum就是答案,如果不在,那么我们把这条边加入这个树上,那么必然会出现环,我们只要把这个环上除掉这条加入边的最大权值边删掉就是答案。现在的关键是怎么求出这个最大权值的边,这里我们可以用树链剖分做,因为树链剖分就是把树链映射到数组上,然后在数组上做查询,也就是线段树,但是这里没有修改操作,只需要查询,那么只需要rmq来在线查询查询即可,用ST算法预处理一下即可,也就是用st替代线段树。
but…最为一个老年人,树剖我已经忘的差不多了,在暑假做过几道题。这题当时我是已经想出具体的实现方法和思路,可是我现在是写不出了(我有模板呀),我对得起理论AC砖家这个称号。
等我再熟悉一下树剖我在把这题写了吧

3.24更新
因为学了倍增的LCA,这题也可以用倍增LCA来做,所以先用这个方法来做啦

#include<iostream>
#include<cstdio>
#include<stack>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#define maxx 200005
#define maxn 18
using namespace std;
struct node
{
    int x,y;
    int w;
    int index;
}edge[maxx];
char vis[maxx];
bool cmp1(node x1,node x2)
{
    return x1.w<x2.w;
}
bool cmp2(node x1,node x2)
{
    return x1.index<x2.index;
}
int n,m;
int pre[maxx];
int findd(int x)
{
    return pre[x]==x?x:pre[x]=findd(pre[x]);
}
vector<int> tree[maxx];
int depth[maxx];
int grand[maxx][maxn],maxW[maxx][maxn];
int N;
void dfs(int root)
{
    for(int i=1;i<=N;i++)
    {
        grand[root][i]=grand[grand[root][i-1]][i-1];
        maxW[root][i]=max(maxW[root][i-1],maxW[grand[root][i-1]][i-1]);
    }
    for(int i=0;i<tree[root].size();i++)
    {
        int now=tree[root][i];
        int v=(edge[now].x==root?edge[now].y:edge[now].x);
        if(v==grand[root][0])
            continue;
        depth[v]=depth[root]+1;
        grand[v][0]=root;
        maxW[v][0]=edge[now].w;
        dfs(v);
    }
}
int lca(int a,int b)
{
    if(depth[a]>depth[b])swap(a,b);
    int ans=0;
    for(int i=N;i>=0;i--)
        if(depth[a]<depth[b]&&depth[a]<=depth[grand[b][i]])
            ans=max(ans,maxW[b][i]),b=grand[b][i];
    for(int i=N;i>=0;i--)
    {
        if(grand[a][i]!=grand[b][i])
        {
            ans=max(ans,maxW[b][i]);
            ans=max(ans,maxW[a][i]);
            a=grand[a][i];
            b=grand[b][i];
        }
    }
    if(a!=b)
    {
        ans=max(ans,maxW[b][0]);
        ans=max(ans,maxW[a][0]);
    }
    return  ans;
}
void init()
{
    N=floor(log2(n));
    for(int i=1;i<=n;i++)
        pre[i]=i;
}
int main()
{

    cin>>n>>m;
    int x,y,w;
    init();
    for(int i=0;i<m;i++)
    {
        scanf("%d%d%d",&x,&y,&w);
        edge[i].x=x;
        edge[i].y=y;
        edge[i].w=w;
        edge[i].index=i;
    }
    sort(edge,edge+m,cmp1);
    long long sum=0;
    for(int i=0;i<m;i++)
    {
        int xx=findd(edge[i].x);
        int yy=findd(edge[i].y);
        if(xx!=yy)
        {
            sum+=edge[i].w;
            pre[xx]=yy;
            tree[edge[i].x].push_back(i);
            tree[edge[i].y].push_back(i);
            vis[edge[i].index]=true;
        }
    }
    //cout<<sum<<endl;
    dfs(1);
    sort(edge,edge+m,cmp2);
    for(int i=0;i<m;i++)
    {
        if(vis[i])
            printf("%lld\n",sum);
        else
            printf("%lld\n",sum+edge[i].w-lca(edge[i].x,edge[i].y));
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值