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

    虽然中途写的时候有点波折,但是最后一发A,还是有点爽。

    这虽然是个模板题,但还是涉及到许多知识的= =

    首先我们求出一个最小生成树,并且求出其边权和(ans)。那么现在考虑加入其它的边,每次加入在树上就会形成一个环,这时因为是一个生成树,所以我们要删去一条边。很明显现在就要删去最小生成树上最大的边即可。

    但这里有个问题,题目要求严格次小。假设现在加入的边权为(w),树上在环中的部分边权最大为(maxw),那么当(w=maxw)时,很显然我们要求一个次大边权(maxw2)来替换;否则直接把(maxw)替换就行了。

    初步思路就是这样,接下来就是怎么去“替换”边,并且统计答案。

    这里我们可以直接倍增地去做就好了(也有种(O(n^2))的dfs算法,但倍增这么优秀,学它做什么

    (g[x][i][0/1])分别表示当前在(x)号结点,向上跳(2^i)个结点,中途经过边权的最大值以及次大值为多少。很显然最大值很容易求,即为

    [max(g[x][i-1][0],g[f[x][i-1]][i-1][0]) ]

    求次大值的话就需要分情况讨论了,直接看代码部分吧,应该比较好理解(其实是我懒得打了

    if(g[v][j - 1][0] == g[f[v][j - 1]][j - 1][0]) {
        g[v][j][1] = max(g[v][j - 1][1], g[f[v][j - 1]][j - 1][1]) ;
    } else if(g[v][j - 1][0] > g[f[v][j - 1]][j - 1][0]) {
        g[v][j][1] = max(g[v][j - 1][1], g[f[v][j - 1]][j - 1][0]) ;
    } else g[v][j][1] = max(g[f[v][j - 1]][j - 1][1], g[v][j - 1][0]) ;
    
    

    那么之后对于每一条边的两个端点,我们就类似于找lca那样倍增地去找次大值就行了。注意这里次大值不能直接取(g[x][i][1])的最大值,也应该像上面那样结合最大值来讨论一下。

    详见代码吧,写得有点长(很多复制粘贴

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 3e5 + 5, M = 3e5 + 5;
    int n, m;
    struct Edge{
        int u, v, w;
        bool operator < (const Edge &A)const{
            return w < A.w;
        }
    }E[M];
    struct edge{
        int v, w, next;
    }e[M];
    int head[N], tot;
    bool check[M] ;
    void adde(int u, int v, int w) {
        e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;
    }
    int ff[N] ;
    int find(int x) {
        return ff[x] == x ? ff[x] : ff[x] = find(ff[x]) ;
    }
    ll ans ;
    int f[N][20], deep[N];
    ll g[N][20][2] ;
    int Log2[N] ;
    void dfs(int u, int fa) {
        deep[u] = deep[fa] + 1;
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v, w = e[i].w;
            if(v == fa) continue ;
            f[v][0] = u;
            g[v][0][0] = w;
            g[v][0][1] = -1e9;
            for(int j = 1; j <= 17; j++) f[v][j] = f[f[v][j - 1]][j - 1] ;
            for(int j = 1; j <= 17; j++) {
                g[v][j][0] = max(g[v][j - 1][0], g[f[v][j - 1]][j - 1][0]) ;
                if(g[v][j - 1][0] == g[f[v][j - 1]][j - 1][0]) {
                    g[v][j][1] = max(g[v][j - 1][1], g[f[v][j - 1]][j - 1][1]) ;
                } else if(g[v][j - 1][0] > g[f[v][j - 1]][j - 1][0]) {
                    g[v][j][1] = max(g[v][j - 1][1], g[f[v][j - 1]][j - 1][0]) ;
                } else g[v][j][1] = max(g[f[v][j - 1]][j - 1][1], g[v][j - 1][0]) ;
            }
            dfs(v, u) ;
        }
    }
    ll LCA(int x, int y) {
        if(deep[x] < deep[y]) swap(x, y) ;
        for(int i = 17; i >= 0; i--) {
            if(deep[f[x][i]] >= deep[y]) x = f[x][i] ;
        }
        if(x == y) return x;
        for(int i = 17; i >= 0; i--) {
            if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i] ;
        }
        return f[x][0] ;
    }
    ll getmx(int x, int y, int k) {
        ll ans1 = 0, ans2 = 0, ans = 0;
        int z = LCA(x, y) ;
        int tx = x, ty = y;
        for(int i = 17; i >= 0; i--)
            if(deep[f[x][i]] >= deep[z]) ans1 = max(ans1, g[x][i][0]), x = f[x][i] ;
        for(int i = 17; i >= 0; i--)
            if(deep[f[y][i]] >= deep[z]) ans2 = max(ans2, g[y][i][0]), y = f[y][i] ;
        if(!k) return max(ans1, ans2) ;
        if(ans1 > ans2) {
            x = tx;
            for(int i = 17; i >= 0; i--)
                if(deep[f[x][i]] >= deep[z]) ans = max(ans, g[x][i][1]), x = f[x][i] ;
            ans = max(ans, ans2) ;
        } else if(ans1 == ans2) {
            x = tx, y = ty;
            ans1 = 0, ans2 = 0;
            for(int i = 17; i >= 0; i--)
                if(deep[f[x][i]] >= deep[z]) ans1 = max(ans1, g[x][i][0]), x = f[x][i] ;
            for(int i = 17; i >= 0; i--)
                if(deep[f[y][i]] >= deep[z]) ans2 = max(ans2, g[y][i][0]), y = f[y][i] ;
            ans = max(ans1, ans2) ;
        } else {
            y = ty;
            for(int i = 17; i >= 0; i--)
                if(deep[f[y][i]] >= deep[z]) ans2 = max(ans2, g[y][i][0]), y = f[y][i] ;
            ans = max(ans, ans1) ;
        }
        return ans ;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0) ;
        cin >> n >> m;
        Log2[1] = 0;
        for(int i = 2; i < N; i++) Log2[i] = Log2[i >> 1] + 1;
        memset(head, -1, sizeof(head)) ;
        for(int i = 1; i <= m; i++) {
            int u, v, w;
            cin >> u >> v >> w;
            E[i] = Edge{u, v, w} ;
        }
        for(int i = 1; i <= n; i++) ff[i] = i;
        sort(E + 1, E + m + 1) ;
        for(int i = 1; i <= m; i++) {
            int u = E[i].u, v = E[i].v, w = E[i].w;
            int fx = find(u), fy = find(v) ;
            if(fx != fy) {
                check[i] = 1;
                ans += w;
                ff[fx] = fy;
                adde(u, v, w); adde(v, u, w);
            }
        }
        dfs(1, 0) ;
        ll mn = 1e18;
        for(int i = 1; i <= m; i++) {
            if(check[i]) continue ;
            int u = E[i].u, v = E[i].v, w = E[i].w;
            int mx = getmx(u, v, 0) ;
            if(mx == w) {
                mx = getmx(u, v, 1) ;
                mn = min(mn, ans - mx + (ll)w) ;
            } else mn = min(mn, ans - mx + (ll)w) ;
        }
        cout << mn ;
        return 0;
    }
    
    
  • 相关阅读:
    Delphi DataSnap入门操作,动起来
    Delphi 记录Record和字符串String相互赋值
    转载:JAVA每天学习
    转载:IntelliJ IDEA 的使用方法总结
    合并多个txt
    如何用vosviewer进行时间线分析——结合pajek
    链路预测(一)
    【js】百分比保留两位小数
    【基础】float保留两位小数
    【js】鼠标悬停显示信息
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10905275.html
Copyright © 2011-2022 走看看