Stree

5 篇文章 0 订阅
4 篇文章 0 订阅

题目

给出n个点,m条有权边,现对于每一条边,你需要回答出包含这条边的最小生成树的总边权值。

分析

首先我们可以构造一个对于这n个点,m条有权边的最小生成树,显然,这是一棵最小的生成树。
那么这棵生成树的边的答案就是这棵生成树的总边权。
然后,就要考虑这棵生成树的其他边了。
在这棵生成树上,如果我们给它加一条新的边,那么,一定会形成一个环。
所以,我们把这个环中最大的边(当然不是新加入的边啦)删掉,这棵新树的总边权就是答案。
怎样删掉这个环中最大的边呢?
发现,实际上就是删掉加入的新边的两个顶点在原树上的路径上最大的边,那么就可以打倍增lca找到最大的边。(想打树链剖分也可以,不拦你)

#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const long long maxlongint=2147483647;
const long long mo=1000000007;
const long long N=200006;
using namespace std;
long long ans[N],way[N][3],n,m,fa[N],sum,bef[N],last[N*2],next[N*2],to[N*2],f[N][20],g[N][20],tot,v[N*2],deep[N];
void bj(long long x,long long y,long long z)
{
    next[++tot]=last[x];
    last[x]=tot;
    to[tot]=y;
    v[tot]=z;
}
void q(long long l,long long r)
{
    long long i=l,j=r,mid=way[(l+r)/2][0],e;
    while(i<j)
    {
        while(way[i][0]<mid) i++;
        while(way[j][0]>mid) j--;
        if(i<=j)
        {
            e=way[i][0];
            way[i][0]=way[j][0];
            way[j][0]=e;
            e=way[i][1];
            way[i][1]=way[j][1];
            way[j][1]=e;
            e=way[i][2];
            way[i][2]=way[j][2];
            way[j][2]=e;
            e=bef[j];
            bef[j]=bef[i];
            bef[i]=e;
            i++;
            j--;
        }
    }
    if(i<r) q(i,r);
    if(l<j) q(l,j);
}
long long get(long long x)
{
    if(fa[x]==x) return x;
    fa[x]=get(fa[x]);
    return fa[x];
}
void dg(long long x,long long y)
{
    for(long long i=last[x];i;i=next[i])
    {
        long long j=to[i];
        if(j!=y)
        {
            deep[j]=deep[x]+1;
            g[j][0]=x;
            f[j][0]=v[i];
            dg(j,x);
        }
    }
}   
long long lca(long long x,long long y)
{
    long long l;
    if(deep[x]<=deep[y])
    {
        l=x;
        x=y;
        y=l;
    }
    long long p=0;
    for(long long i=log2(n);i>=0;i--)
    {
        if(deep[g[x][i]]>=deep[y])
        {
            p=max(p,f[x][i]);
            x=g[x][i];
        }
    }
    for(long long i=log2(n);i>=0;i--)
    {
        if(g[x][i]!=g[y][i])
        {
            p=max(p,max(f[x][i],f[y][i]));
            x=g[x][i];
            y=g[y][i];
        }
    }
    if(x!=y) p=max(p,max(f[x][0],f[y][0]));
    return p;
}
int main()
{
    freopen("street.in","r",stdin);
    freopen("street.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for(long long i=1;i<=m;i++)
    {
        scanf("%lld%lld%lld",&way[i][1],&way[i][2],&way[i][0]);
        fa[i]=i;
        bef[i]=i;
    }
    q(1,m);
    long long k=0;
    sum=0;
    for(long long i=1;i<=m,k<n-1;i++)
    {
        long long x=get(way[i][1]);
        long long y=get(way[i][2]);
        if(x==y) continue;
        k++;
        ans[bef[i]]=1;
        bj(way[i][1],way[i][2],way[i][0]);
        bj(way[i][2],way[i][1],way[i][0]);
        sum+=way[i][0];
        fa[x]=y;
    }
    deep[1]=1;
    dg(1,0);
    for(long long i=1;i<=log2(n);i++)
        for(long long j=1;j<=n;j++)
        {
            g[j][i]=g[g[j][i-1]][i-1];
            f[j][i]=max(f[j][i-1],f[g[j][i-1]][i-1]);
        }
    for(long long i=1;i<=m;i++)
    {
        if(!ans[bef[i]])
        {
            ans[bef[i]]=sum-lca(way[i][1],way[i][2])+way[i][0];
        }
        else 
            ans[bef[i]]=sum;
    }
    for(long long i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值