[Contest1336]管道(pipe)

题面

Description

给你一个城市下水道网络图,你需要选出一些管道,使得在只使用这些管道的情况下,令整个网络联通,并且花费最小。

网络图可以看做是无向连通图,有n个节点和m条边,每条边连接ui和vi,选择的花费是wi。

不巧的是,由于某些原因,现在市政局要求选定某条特定的边管道,你的任务是求出对于某一条边,在选择这条管道的前提下的最小花费。

 

Input

第1行包含两个整数n,m,表示点数和边数。

第2~m+1行每行三个整数ui,vi,wi,表示有一条管道连接ui和vi,费用为wi。

 

Output

输出m行,每行一个整数,表示选择第i条管道的前提下的最小花费。

管道按输入的顺序编号为1~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

 

Hint

对于20%的数据,n<=1000,m<=2000

对于另外20%的数据,m<=n+10

对于100%的数据,2<=n<=100000,1<=m<=200000

保证初始图连通

题意

每次选1条边必须出现,求最小生成树。

题解

简单模拟后发现可以先求出最小生成树,记录最小生成树中的边,每选择1条边时,

如果存在于最小生成树中,答案就是最小生成树;

否则,这条边连接的两个点必定和它们的LCA构成一个环,所以我们应该选择其中最大的一条边删除,可以通过倍增预处理,在lca的时候取最值。

#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e5+5;
long long n,m,cnt,head[N],fa[N],dp[N],f[N][20],mx[N][20];
long long sum;
bool vis[N];
struct edge1{
    long long u,v,w,id;
}e1[N<<2];
struct edge2{
    long long v,w,next;
}e2[N<<2]; 
void add(int u,int v,long long w){
    e2[++cnt]=(edge2){v,w,head[u]};
    head[u]=cnt;
}
bool cmp(edge1 a,edge1 b){
    return a.w<b.w;
}
void dfs(int u){
    for(int i=head[u];i;i=e2[i].next){
        int v=e2[i].v;
        if(v==f[u][0])continue;
        f[v][0]=u;
        mx[v][0]=e2[i].w;
        dp[v]=dp[u]+1;
        dfs(v);
    }
}
long long lca(int x,int y){
    long long ans=0;
    if(dp[x]<dp[y])swap(x,y);
    for(int i=17;i>=0;i--){
        if(dp[f[x][i]]>=dp[y]){
            ans=max(ans,1ll*mx[x][i]);
            x=f[x][i];
        }
    }
    if(x==y)return ans;
    for(int i=17;i>=0;i--){
        if(f[x][i]!=f[y][i]){
            ans=max(ans,1ll*max(1ll*mx[x][i],1ll*mx[y][i]));
            x=f[x][i];y=f[y][i];
        }
    }
    ans=max(ans,1ll*max(1ll*mx[x][0],1ll*mx[y][0]));
    return ans;
}
int get(int x){
    return x==fa[x]?x:fa[x]=get(fa[x]);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){//建两层边,为了处理答案方便
        int u,v,w;
        scanf("%d%d%lld",&e1[i].u,&e1[i].v,&e1[i].w);
        e1[i].id=i;
        e1[i+m]=e1[i];
    }
    sort(e1+1+m,e1+1+m+m,cmp);
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=m+1;i<=m*2;i++){//最小生成树
        int u=get(e1[i].u),v=get(e1[i].v);
        if(u!=v){
            fa[u]=v;
            sum+=(long long)e1[i].w;
            vis[e1[i].id]=1;
            add(e1[i].u,e1[i].v,e1[i].w);//建树
            add(e1[i].v,e1[i].u,e1[i].w);
        }
    }
    dfs(1);//深搜,lca预处理
    for(int i=1;i<=17;i++){
        for(int j=1;j<=n;j++){
            f[j][i]=f[f[j][i-1]][i-1];
            mx[j][i]=max(1ll*mx[j][i-1],1ll*mx[f[j][i-1]][i-1]);
        }
    }
    for(int i=1;i<=m;i++){//两种情况
        if(vis[i])printf("%lld\n",sum);
        else printf("%lld\n",sum-(long long)lca(e1[i].u,e1[i].v)+e1[i].w);
    }
}

  

posted @ 2019-08-23 15:50  Evan704  阅读(224)  评论(0编辑  收藏  举报