JZOJ.4596 Stree

Problem

Description

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

Input

第一行两个数n,m
接下来m行i,j,k,表示i与j间有一条权值为k的边

Output

m行答案

Sample Input

5 7
1 2 3
1 3 1
1 4 5
2 3 2
2 5 3
3 4 2
4 5 4

Sample Output

9
8
11
8
8
8
9

Data Constraint

30% n<=1000
100% n,m<=200000

Solution

注释:MST为最小生成树。
首先我们将最小生成树求出来,那么接下来如果询问的这条边在最小生成树里面,则答案为MST的权值和。否则我们想一下,树是由n个点n-1条边连接而成的,如果加上这条边,则这棵树成为了一个图。因此我们要将这棵树的某一条边删掉,加上被询问的这条边。那么显然,将要被删掉的那条边就是被询问的边的两点最短距离的最大权值的边。我们求一下LCA就行了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define N 200010
using namespace std;
ll deep[N],go[N*2],head[N*2],next[N*2],v[N*2],f[N],tr[N*2][2];
int i,j,k,l,n,m,t,f1,f2,tot;
ll ans,sum,temp;
struct note{
    ll x,y,z;
};
note a[N],b[N];
bool cnt(note x,note y) {return x.z<y.z;}
int get(int n)
{
    if (f[n]==n) return n;
    {
        f[n]=get(f[n]);
        return f[n];
    }
}
void lb(ll x,ll y,ll z)
{
    go[++tot]=y;
    next[tot]=head[x];
    head[x]=tot;
    v[tot]=z;
}
void bs(int x,int y,int z)
{
    deep[x]=z;
    int i;
    for (i=head[x];i;i=next[i])
    {
        int now=go[i];
        if (now!=y)
        {
            tr[now][0]=x;
            tr[now][1]=v[i];
            bs(now,x,z+1);
        }
    }
}
ll lca(int x,int y)
{
    ll mx=-2147483647;
    if (deep[x]<deep[y]) swap(x,y);
    while (deep[x]>deep[y])
    {
        mx=max(mx,tr[x][1]);
        x=tr[x][0];
    }
    if (x==y) return mx;
    while (x!=y)
    {
        mx=max(mx,tr[x][1]);
        mx=max(mx,tr[y][1]);
        x=tr[x][0];
        y=tr[y][0];
    }
    return mx;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (i=1;i<=m;i++) scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].z);
    for (i=1;i<=n;i++) f[i]=i;
    memcpy(b,a,sizeof(a));
    sort(a+1,a+m+1,cnt);
    k=tot=0;
    for (i=1;i<=m;i++)
    {
        f1=get(a[i].x);
        f2=get(a[i].y);
        if (f1!=f2)
        {
            f[f2]=f1;
            sum+=a[i].z;
            lb(a[i].x,a[i].y,a[i].z);
            lb(a[i].y,a[i].x,a[i].z);
            k++;
        }
        if (k==n) break;
    }
    bs(1,0,0);
    for (i=1;i<=m;i++)
    {
        temp=lca(b[i].x,b[i].y);
        ans=sum-temp+b[i].z;
        printf("%lld\n",ans);
    }
}

——2016.7.9

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值