Street

Description

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

Solution

题解和题意一样简洁系列。
首先求出mst,然后对于每一条不在mst里面的边,相当于把它和mst中的一条边替换。
若是(x,y)这条边,那么就是在生成树中x到y的路径上选择一条边权最大的边替换。
倍增最大值即可。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define N 200005
using namespace std;
typedef long long ll;
struct note{int x,y,v,id;}e[N];
bool cmp(note x,note y) {return x.v<y.v;}
int n,m,x,y,z,tot,cnt,l,q[N],ask[N],d[N],fa[N];
int last[N],v[N*2],next[N*2],t[N*2],f[N][18],g[N][18];
bool bz[N];
ll ans,an[N];
int get(int x) {return fa[x]?fa[x]=get(fa[x]):x;}
void add(int x,int y,int z) {
    t[++l]=y;v[l]=z;next[l]=last[x];last[x]=l;
}
int lca(int x,int y) {
    if (d[x]<d[y]) swap(x,y);int ans=0;
    fd(j,17,0) if (d[g[x][j]]>d[y]) ans=max(ans,f[x][j]),x=g[x][j];
    if (d[x]!=d[y]) ans=max(ans,f[x][0]),x=g[x][0];
    fd(j,17,0) if (g[x][j]!=g[y][j]) 
    ans=max(ans,max(f[x][j],f[y][j])),x=g[x][j],y=g[y][j];
    if (x!=y) return max(ans,max(f[x][0],f[y][0]));else return ans;
}
int main() {
    freopen("street.in","r",stdin);
    freopen("street.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,m) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v),e[i].id=i;
    sort(e+1,e+m+1,cmp);
    fo(i,1,m) {
        int a=get(e[i].x),b=get(e[i].y);
        if (a!=b) fa[a]=b,tot++,ans+=(ll)e[i].v,bz[i]=1;
        if (tot==n-1) break;
    }
    fo(i,1,m) if (bz[i]) {
        add(e[i].x,e[i].y,e[i].v);
        add(e[i].y,e[i].x,e[i].v);
        an[e[i].id]=ans;
    } else ask[++cnt]=i;
    int i=0,j=1;q[1]=d[1]=1;
    while (i<j) 
        rep(k,q[++i]) if (t[k]!=g[q[i]][0]) {
            g[t[k]][0]=q[i];f[t[k]][0]=v[k];
            d[t[k]]=d[q[i]]+1;q[++j]=t[k];
        } 
    fo(j,1,17) fo(i,1,n) g[i][j]=g[g[i][j-1]][j-1],
    f[i][j]=max(f[i][j-1],f[g[i][j-1]][j-1]);
    fo(i,1,cnt) {
        int id=ask[i];
        an[e[id].id]=ans+(ll)e[id].v;
        an[e[id].id]-=(ll)lca(e[id].x,e[id].y);
    }
    fo(i,1,m) printf("%lld\n",an[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值