【Codeforces 609E】【Stree】【最小生成树】

题目大意

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

题解

一个显然的结论,无论怎么样,生成树与最小生成树不同的边最多为一条。求出最小生成树,枚举加入哪条边,用倍增算法求出所加边在树上路径边权的最大值,更改答案即可。

code

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=200000,maxm=200000;
int n,m,gra,to[maxn*2+10],val[maxn*2+10],next[maxn*2+10],begin[maxn+10],dep[maxn+10],
    father[maxn+10][20],mx[maxn+10][20],fa[maxn+10],queue[maxn+10];
bool inqueue[maxn+10];
long long ans[maxm+10];
struct rec{int u,v,w,num;};
rec edge[maxm+10];
bool cmp(rec i,rec j){
    return (i.w<j.w)||((i.w==j.w)&&(i.u<j.u))||((i.w==j.w)&&(i.u==j.u)&&(i.v<j.v));
}
int getfather(int x){
    if(!fa[x])return x;
    return fa[x]=getfather(fa[x]);
}
void insert(int u,int v,int w){
    to[++gra]=v;
    val[gra]=w;
    next[gra]=begin[u];
    begin[u]=gra;
}
void bfs(int s){
    int head=0,tail=0;
    inqueue[queue[++tail]=s]=1;
    for(;head!=tail;){
        int now=queue[++head];
        for(int i=begin[now];i;i=next[i])
            if(!inqueue[to[i]]){
                dep[to[i]]=dep[now]+1;
                father[to[i]][0]=now;
                mx[to[i]][0]=val[i];
                inqueue[queue[++tail]=to[i]]=1;
            }
    }
}
int lc(int u,int v){
    int ans=0;
    if(dep[u]<dep[v])swap(u,v);
    fd(i,log(n)/log(2),0)
        if(dep[father[u][i]]>=dep[v]){
            ans=max(ans,mx[u][i]);
            u=father[u][i];
        }
    if(u==v)return ans;
    fd(i,log(n)/log(2),0)
        if(father[u][i]!=father[v][i]){
            ans=max(ans,max(mx[u][i],mx[v][i]));
            u=father[u][i];
            v=father[v][i];
        }
    return max(ans,max(mx[u][0],mx[v][0]));
}
int main(){
    //freopen("street.in","r",stdin);
    //freopen("street.out","w",stdout);
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,m)
        scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w),edge[i].num=i;
    sort(edge+1,edge+m+1,cmp);
    fo(i,1,m){
        int fu=getfather(edge[i].u),fv=getfather(edge[i].v);
        if(fu!=fv){
            ans[0]+=edge[i].w;
            ans[edge[i].num]=-1;
            fa[fu]=fv;
            insert(edge[i].u,edge[i].v,edge[i].w);
            insert(edge[i].v,edge[i].u,edge[i].w);
        }
    }
    dep[1]=1;
    bfs(1);
    fo(j,1,log(n)/log(2))
        fo(i,1,n){
            father[i][j]=father[father[i][j-1]][j-1];
            mx[i][j]=max(mx[i][j-1],mx[father[i][j-1]][j-1]);
        }
    fo(i,1,m)
        if(ans[edge[i].num]==-1)ans[edge[i].num]=ans[0];
        else ans[edge[i].num]=ans[0]+edge[i].w-lc(edge[i].u,edge[i].v);
    fo(i,1,m)printf("%lld\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值