zoukankan      html  css  js  c++  java
  • 洛谷 P4180 【模板】严格次小生成树[BJWC2010]

    题目描述

    小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) 

    这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

    输入输出格式

    输入格式:

    第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

     

    输出格式:

    包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

     

    输入输出样例

    输入样例#1: 复制
    5 6
    1 2 1 
    1 3 2 
    2 4 3 
    3 5 4 
    3 4 3 
    4 5 6 
    输出样例#1: 复制
    11

    说明

    数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

    这道题直接说做法吧

    首先我们需要知道一个性质 次小生成树的树边只和最小生成树有一边之差 别问我正确性我也不知道为什么 显然求证法...??

    所以首先我们先cao一颗最小生成树出来 在考虑到底是哪条非树边替换哪条树边

    所以枚举每一条非树边$(u, v)$ 在树上找到连接这两点的树链 寻找该链上的最大值 直接进行替换即可

    为什么可以直接替换呢 因为既然他是最小生成树 那条链上的每条边都必然小于等于当前$(u, v)$ 但是会出现一个问题

    题目要求的是 严格次小 若是最大值与$val(u, v)$相等 那么替换之后就不是严格次小了 所以要解决这个问题 不仅需要维护链上的最大值 还要维护次大值

    若是$val(u, v) == max$ 那么我们就替换次大边即可 

    可以使用树剖套线段树维护这个 倍增也可以

    代码(树剖)

    #include <bits/stdc++.h>
    #define oo 1e18
    using namespace std;
    
    typedef long long ll;
    const int N = 1e6 + 6;
    int fa[N], head[N], nex[2 * N], tov[2 * N], val[2 * N], fat[N];
    int tot, dep[N], son[N], size[N], in[N], idc, top[N], seq[N], w[N];
    int n, m;
    bool vis[3 * N];
    ll sum = 0;
    
    struct edge {
        int u, v, w;
    }e[3 * N];
    
    struct segment {
        int ma1, ma2;
    }f[4 * N], ans;
    
    bool cmp(const edge & a, const edge & b) {
        return a.w < b.w;
    }
    
    int find_fa(int u) {
        return u == fat[u] ? u : fat[u] = find_fa(fat[u]);
    }
    
    void add(int u, int v, int w) {
        
        tot ++;
        nex[tot] = head[u];
        tov[tot] = v;
        val[tot] = w;
        head[u] = tot;
    }
    
    void dfs1(int u, int fat) {
        
        fa[u] = fat; size[u] = 1;
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if(v == fat) continue;
            dep[v] = dep[u] + 1;
            w[v] = val[i];
            dfs1(v, u); 
            size[u] += size[v];
            if(size[v] > size[son[u]]) son[u] = v;
        }
    }
    
    void dfs2(int u, int tp) {
        
        in[u] = ++ idc; 
        top[u] = tp; seq[idc] = u;
        if(son[u]) dfs2(son[u], tp);
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if(v == fa[u]) continue;        
            if(v != son[u]) dfs2(v, v);
        }
    }
    
    void update(int o) {
        
        int ma = max(f[2 * o].ma1, f[2 * o + 1].ma1);
        f[o].ma1 = ma;
        f[o].ma2 = max((ma == f[2 * o].ma1 ? f[2 * o].ma2 : f[2 * o].ma1), 
        (ma == f[2 * o + 1].ma1 ? f[2 * o + 1].ma2 : f[2 * o + 1].ma1));
    }
    
    void build(int o, int l, int r) {
        
        if(l == r) { f[o].ma1 = w[seq[l]]; f[o].ma2 = -1; return ;}
        int mid = l + r >> 1;
        build(2 * o, l, mid); 
        build(2 * o + 1, mid + 1, r);
        update(o);
    }
    
    void Init( ) {
        
        scanf("%d%d",& n,& m);
        for(int i = 1;i <= m;i ++) 
            scanf("%d%d%d",& e[i].u, & e[i].v, & e[i].w);
        sort(e + 1, e + m + 1, cmp);
        for(int i = 1;i <= n;i ++) fat[i] = i;
        for(int i = 1;i <= m;i ++) {
            int fa1 = find_fa(e[i].u), fa2 = find_fa(e[i].v);
            if(fa1 == fa2) continue;
            fat[fa2] = fa1; vis[i] = true; sum += e[i].w;
            add(e[i].u, e[i].v, e[i].w); add(e[i].v, e[i].u, e[i].w);
        }
        dfs1(1, 0); dfs2(1, 0); build(1, 1, n);
    }
    
    void change(segment a) {
        
        int ma = max(a.ma1, ans.ma1);
        ans.ma2 = max((ans.ma1 == ma ? ans.ma2 : ans.ma1), (a.ma1 == ma ? a.ma2 : a.ma1));
        ans.ma1 = ma;
    }
    
    segment query(int o, int l, int r, int L, int R) {
        
        if(l >= L && r <= R) return f[o];
        int mid = l + r >> 1;
        if(R <= mid) return query(2 * o, l, mid, L, R);
        if(L > mid)  return query(2 * o + 1, mid + 1, r, L, R);
        segment q1 = query(2 * o, l, mid, L, R), q2 = query(2 * o + 1, mid + 1, r, L, R), q3;
        int ma = max(q1.ma1, q2.ma1); q3.ma1 = ma;
        q3.ma2 = max((ma == q1.ma1 ? q1.ma2 : q1.ma1), (ma == q2.ma1 ? q2.ma2 : q2.ma1));
        return q3;
    }
    
    segment Query(int u, int v) {
        
        ans.ma1 = 0, ans.ma2 = -1;
        while(top[u] != top[v]) {
            if(dep[top[u]] < dep[top[v]]) swap(u, v);
            change(query(1, 1, n, in[top[u]], in[u]));
            u = fa[top[u]];
        }
        if(dep[u] < dep[v]) swap(u, v);
        if(u != v) change(query(1, 1, n, in[v] + 1, in[u]));
        return ans;
    }
    
    void Solve( ) {
        
        ll Ans = oo;
        for(int i = 1;i <= m;i ++) {
            if(vis[i]) continue;
            segment ma = Query(e[i].u, e[i].v);
            ll del;
            if(ma.ma1 == e[i].w) del = ma.ma2; else del = ma.ma1;
            if(del == -1) continue;
            Ans = min(Ans, sum - del + 1ll * e[i].w);
        }
        printf("%lld
    ", Ans);
    }
    
    int main( ) {
        
        Init( );
        Solve( );
    }
  • 相关阅读:
    前端开发和网页设计的过去和未来
    Web开发人员vs网页设计师
    Linux最终将会领先于Windows、Mac OS!
    Linux 大爆炸:一个内核,无数发行版
    因PHP漏洞,超过4.5万个中国网站被攻击
    在 Linux 中自动配置 IPv6 地址
    echart-折线图,数据太多想变成鼠标拖动和滚动的效果?以及数据的默认圈圈如何自定义圆圈的样式
    用TweenMax.js动画让数字动起来
    zrender笔记----(数字Number组件)出现的问题和解决办法
    面试题常考&必考之--js中的数组去重和字符串去重
  • 原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9835911.html
Copyright © 2011-2022 走看看