codeforces 609E. Minimum spanning tree for each edge

题目大意: 求最小生成树的权值,其中第i条边必须选上

分析:第i条边被选上的时候,如果它已经被选入了我们的最小生成树的边,那么,我们直接输出原有的总权值就行, 所以,可以用一个VIS数组标记那些边选上了。如果,第i条边没被选上,选上后树中某个位置会扣成环,因此在加上第i条边之前,先用LCA找到第i条边两个点的公共祖先,祖先与这两个点会形成两条链,再找到两条链中权值最大的边去掉后加上第i条边就是answer,注意,如不用倍增找最长的边长,而是一段一段的找会在第51个样例TLE

 //AC 代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 120;
struct node{
    int to;
    ll w;
}tem;
struct pr{
    int next;
    ll len;
}tp, ft[maxn];
struct ver{
    int from;
    int to;
    int id;
    ll w;
}g[maxn], ls[maxn];
bool cmp(struct ver a, struct ver b){
    return  a.w < b.w;
}
vector<struct node>edge[maxn];
int dep[maxn];
int fa[maxn][30];
ll  mlen[maxn][30]; 
int father[maxn];
int vis[maxn];
int judge[maxn];
void dfss(int x){
     judge[x] = 1;
     for(int i = 0; i < edge[x].size(); i++){
         if(judge[edge[x][i].to] == 0){
             int sx = edge[x][i].to;
             if(fa[sx][0] == x){
                 ft[sx].next = x;
                 ft[sx].len = edge[x][i].w;
             }else if(fa[x][0] == sx){
                 ft[x].next = sx;
                 ft[x].len = edge[x][i].w;
             }
             dfss(edge[x][i].to);
         }
     }
}
void dfs(int x, int fat){
     dep[x] = dep[fat] + 1;
        fa[x][0] = fat;
        for(int i = 0; i < edge[x].size(); i++){
           if(edge[x][i].to == fat)    continue;
        dfs(edge[x][i].to, x);    
        }
}
void init(){
    for(int i = 0; i < maxn; i++)   father[i] = i;
}
int find(int x){
    if(x == father[x])   return x;
    else{
        father[x] = find(father[x]);
        return  find(father[x]);
    }
}
ll sum = 0;
void kruscal(int n, int m){
    init();
    int cnt = 0;  
    for(int i = 1; i <= m; i++){
        if(cnt == n - 1)   break; 
        int x = g[i].from;   int y = g[i].to;    ll ew = g[i].w; 
        if(find(x) != find(y)){
             tem.to = y;  tem.w = ew;
             edge[x].push_back(tem);
             tem.to = x;
             edge[y].push_back(tem);
             sum = sum + ew;
             cnt++;
             father[find(y)] = find(x);
             vis[g[i].id] = 1;
        }else   continue;
    }    
}
int lca(int x, int y){
    if(x == y)    return x;
    if(dep[y] > dep[x])   swap(x, y);
    for(int i = 20; i >= 0; i--){
        if(fa[x][i]&&dep[fa[x][i]] >= dep[y])   x = fa[x][i];
      }
      if(x == y)    return x;
      for(int i = 20; i >= 0; i--){
          if(fa[x][i] != 0 && fa[y][i] != 0 && fa[x][i] != fa[y][i]){
                x = fa[x][i];
                y = fa[y][i];
          }
      }
      return  fa[x][0];
}
void len_init(int n){
    for(int x = 1; x <= n; x++){
        mlen[x][0] = ft[x].len;
    }
    for(int i = 1; i <= 20; i++){
        for(int x = 1; x <= n; x++){
            mlen[x][i] = max(mlen[x][i - 1], mlen[fa[x][i - 1]][i - 1]);
        }
    }
}
ll querymax(int x, int du){
    ll ml = 0;
    for(int i = 20; i >= 0; i--){
        if(dep[fa[x][i]] < dep[du])   continue;
        else if(dep[fa[x][i]] == dep[du]){
            ml = max(ml, mlen[x][i]);
            break;
        }else{
            ml = max(ml, mlen[x][i]);
            x = fa[x][i];
        }
    }
    return ml;

int main(){
    memset(vis, 0, sizeof(vis));
    memset(judge, 0, sizeof(judge));
    int n, m;
    cin>>n>>m;
    for(int i = 1; i <= m; i++){
        scanf("%d%d%lld", &g[i].from, &g[i].to, &g[i].w);
        g[i].id = i;   ls[i] = g[i];
    }
    sort(g + 1, g + 1 + m, cmp);
    kruscal(n, m);
    dep[0] = -1;
    dfs(1, 0);
    for(int i = 1; i <= 20; i++){
        for(int j = 1; j <= n; j++){
            fa[j][i] = fa[fa[j][i - 1]][i - 1];
        }
    }
    dfss(1);
    ft[1].next = 0;
    ft[1].len = 0;
    len_init(n);
    for(int i = 1; i <= m; i++){
        if(vis[ls[i].id]){
            printf("%lld\n", sum);
            continue;
        }else{
            int du = lca(ls[i].from, ls[i].to);
            int x = ls[i].from;
            int y = ls[i].to;
            ll diff = max(querymax(x, du), querymax(y, du));
            ll ans = sum - diff + ls[i].w;
            printf("%lld\n", ans);
        }   
    }
    return 0;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值