• 严格次小生成树(模板)-洛谷4180


    传送门

    dr就这样咕咕咕了好几天

    (一时咕,一时爽,一直咕,那就gg了)

    我才不会承认

    我看题(概念)看了好久都没懂

    -----------------------------------------------------

    一、定义

      给出一个图

      可以形成若干个树

      将形成的树 按总比边权和的大小 从小到大排序

      第二小的就是次小生成树

    这时需要注意啦:

      若只是按排完序后的第二个树

      得到的是 不严格次小生成树

      因为 有可能排序后的第二个树 和 第一个树相等

      那么

      严格次小生成树 就是 和 第一棵树不等的 剩余树中的 最小的树

    (终于整明白定义了,就开始想想怎么做吧)

    二、算法

    先求出最小生成树

    (个人喜欢用kruskal)

    然后枚举出非树边

    把非树边加入到树中

    就会形成一个环

    就要删边

    (看看 哪次 加入非树边再删边 得到的生成树 是最小的<当然要大于最小生成树啦>)

    (每枚举到一条非树边 就是 一种情况的(一棵树)的判断)

    删边的原则?

    如果加进来的边与环上最大值不同,直接删掉最大值

    如果加进来的边与环上最大值相同,就删掉次大值

    如何高效的去找要删的边咧?(维护最大值和次大值)

    倍增LCA!!

    fa维护LCA

    maxi维护最大值

    mini维护次大值

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #define ll long long
    using namespace std;
    
    const ll N = 400010;
    const ll M = 900010;
    const ll INF = 2147483647000000;
    
    inline ll read()//快读 
    {
        ll sum = 0,p = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-')
                p = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            (sum *= 10) += ch - '0';
            ch = getchar();
        }
        return sum * p;
    }
    
    struct edge
    {
        ll u,v,d;
        ll next;
    }G[N << 1];
    ll tot,head[N];
    edge A[M<<1];
    bool vis[M<<1];
    ll fa[N];
    ll f[N][19]; 
    ll maxi[N][19];
    ll mini[N][19];
    ll deep[N];
    ll n,m;
    
    inline bool cmp(edge x,edge y)//快排 
    {
        return x.d < y.d;
    } 
    
    inline ll getfa(ll o)//找根节点 
    {
        if(o == fa[o])
            return o;
        else
            return fa[o] = getfa(fa[o]);
    }
    
    inline void add(ll u,ll v,ll d)//加边 
    {
        G[++tot].u = u;
        G[tot].v = v;
        G[tot].d = d;
        G[tot].next = head[u];
        head[u] = tot;
    }
    
    inline void dfs(ll o,ll x)//预处理 
    {
        f[o][0] = x;
        for(ll i = head[o];i;i = G[i].next)
        {
            ll v = G[i].v;
            if(v == x)
                continue;
            deep[v] = deep[o] + 1LL;
            maxi[v][0] = G[i].d;
            mini[v][0] = -INF;
            dfs(v,o);
        }
    }
    
    inline void cal()//倍增的思想求出最大值和次大值 
    {
        for(ll i = 1;i <= 18;i++)
            for(ll j = 1;j <= n;j++)
            {
                f[j][i] = f[f[j][i - 1]][i - 1];
                maxi[j][i] = max(maxi[j][i - 1],maxi[f[j][i - 1]][i - 1]);
                mini[j][i] = max(mini[j][i - 1],mini[f[j][i - 1]][i - 1]);
                if(maxi[j][i - 1] > maxi[f[j][i - 1]][i - 1])
                    mini[j][i] = max(mini[j][i],maxi[f[j][i - 1]][i - 1]);
                else
                if(maxi[j][i - 1] < maxi[f[j][i - 1]][i - 1])
                    mini[j][i] = max(mini[j][i],maxi[j][i - 1]);
            }
    }
    
    inline ll lca(ll x,ll y)//倍增lca 
    {
        if(deep[x] < deep[y])
            swap(x,y);
        for(ll i = 18;i >= 0;i--)
            if(deep[f[x][i]] >= deep[y])
                x = f[x][i];
        if(x == y)
            return x;
        for(ll i = 18;i >= 0;i--)
            if(f[x][i] ^ f[y][i])
            {
                x = f[x][i];
                y = f[y][i];
            }
        return f[x][0];
    }
    
    inline ll qmax(ll u,ll v,ll maxx)//找最大值(或次大值)-- 倍增的思想 
    {
        ll ans = -INF;
        for(ll i = 18;i >= 0;i--)
        {
            if(deep[f[u][i]] >= deep[v])
            {
                if(maxx != maxi[u][i])
                    ans = max(ans,maxi[u][i]);
                else
                    ans = max(ans,mini[u][i]);
                u = f[u][i];
            }
        }
        return ans;
    }
    
    int main()
    {
        n = read(),m = read();
        for(ll i  = 1;i <= m;i++)
            A[i].u = read(),A[i].v = read(),A[i].d = read();
        sort(A+1,A+m+1,cmp);
        for(ll i = 1;i <= n;i++)
            fa[i] = i;
        ll cnt = 0LL;
        for(ll i = 1;i <= m;i++)//kruscal 
        {
            ll u = getfa(A[i].u);
            ll v = getfa(A[i].v);
            if(u != v)
            {
                cnt += A[i].d;
                fa[u] = v;
                add(A[i].u,A[i].v,A[i].d);
                add(A[i].v,A[i].u,A[i].d);
                vis[i] = true;
            }
        }
        mini[1][0] = -INF;
        deep[1] = 1;
        dfs(1,-1);
        cal();//维护最大值和次大值 
        ll ans = INF;
        for(ll i = 1;i <= m;i++)
        {
            if(!vis[i])//没有被加过边(不在生成图中) 
            {
                ll u = A[i].u;
                ll v = A[i].v;
                ll d = A[i].d;
                ll o = lca(u,v);
                ll maxu = qmax(u,o,d);
                ll maxv = qmax(v,o,d);
                ans = min(ans,cnt - max(maxu,maxv) + d);
            }
        } 
        printf("%lld",ans);
        return 0;
    }
  • 相关阅读:
    Selenium的使用
    Redis防护建议
    爬虫文件存储-3:Redis
    爬虫文件存储-2:MongoDB
    爬虫文件存储-1:mysql
    爬虫文件存储:txt文档,json文件,csv文件
    Python MongoDB 教程
    使用Robo 3T 软件管理MongoDB数据库如何执行命令行shell
    使用Scrapy爬取图书网站信息
    解决Scrapy抓取中文网页保存为json文件时中文不显示而是显示unicode的问题
  • 原文地址:https://www.cnblogs.com/darlingroot/p/10666595.html
Copyright © 2020-2023  润新知