zoukankan      html  css  js  c++  java
  • [模板] 严格次小生成树

    题目描述

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

    Solution

    首先,我们得知道最小生成树和次小生成树只差一条边,我不会证明,想学的可以去网上找。严格次小生成树大致思路就是Kruskal+倍增LCA。
    我们先用KrusKal求出最小生成树,标记一下哪些边用过,哪些边没用过,
    我们肯定是用没用过的边去替换用过的边。
    我们先看样例最小生成树的图片。

    接下来我们枚举每一条不在最小生成树的边,枚举第一条,如图:

    我们可以发现1,2,3,4成为了一个环,删掉任意一个环上的边都能形成生成树,我们容易得到新加入的这条边肯定是环上的最大值,(不然之前求的就不是最小生成树了)。我们只需换掉环上的最大的那条边就行了,但它要求严格次小,所以换的那条边不能等于当前这条边,所以我们还需维护一个严格次大值。那么我们怎么求解路径最大值和次大值呢?用倍增LCA维护一个最大值和次大值,就可以求解了。代码如下:

    // luogu-judger-enable-o2
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    ll last[100010],fa[201010],len,ans,dep[201010],f[201001][20],n,m;
    ll mx1[201000][20],mx2[201000][20],vis[301000],pai[10],anss=100000000;
    struct node
    {
        ll to,next,w;
    }a[501010];
    struct kzj
    {
        ll x,y,z;
        bool operator< (const kzj &c) const{return c.z>z;}
    }ff[301010];
    void add(ll a1,ll a2,ll a3)
    {
        a[++len].to=a2;
        a[len].w=a3;
        a[len].next=last[a1];
        last[a1]=len;
    }
    ll find(ll x)
    {
        if(x==fa[x]) return x;
        return fa[x]=find(fa[x]);
    }
    void dfs(ll x,ll father)
    {
        for(ll i=last[x];i;i=a[i].next)
        {
            ll to=a[i].to;
            if(to==father) continue;
            dep[to]=dep[x]+1;
            f[to][0]=x;
            mx1[to][0]=a[i].w;
            dfs(to,x);
        }
    }
    bool cmp(ll a1,ll a2){return a1>a2;}
    void zuxian()
    {
        for(ll j=1;j<=19;j++)
        for(ll i=1;i<=n;i++)
        {
            ll tot=0;
            f[i][j]=f[f[i][j-1]][j-1];
            pai[++tot]=mx1[i][j-1];
            pai[++tot]=mx2[i][j-1];
            pai[++tot]=mx1[f[i][j-1]][j-1];
            pai[++tot]=mx2[f[i][j-1]][j-1];
            sort(pai+1,pai+tot,cmp);
            mx1[i][j]=pai[1];
            for(ll k=2;k<=tot;k++)
            if(pai[k]!=pai[1])
            {mx2[i][j]=pai[k];break;}
        }
    }
    void lca(ll x,ll y,ll kk)
    {	
        ll ans1=0,ans2=0;
        if(dep[x]<dep[y]) swap(x,y);
        ll s=dep[x]-dep[y];
        for(ll i=0;i<=19;i++)
        if(s&(1<<i)) 
        {
            if(mx1[x][i]>ans1)
            {
                ans2=ans1,ans1=mx1[x][i];
                if(mx2[x][i]>ans2)
                ans2=mx2[x][i];
            }
            else if(mx1[x][i]>ans2)
            ans2=mx1[x][i];
            x=f[x][i];
        }
        if(x==y) 
        {
            if(ans1!=ff[kk].z)
            anss=min(ff[kk].z-ans1,anss);
            else
            anss=min(ff[kk].z-ans2,anss);
            return;
        }
        for(ll i=19;i>=0;i--)
        {
            if(f[x][i]!=f[y][i])
            {
                if(mx1[x][i]>ans1)
                {
                    ans2=ans1,ans1=mx1[x][i];
                    if(mx2[x][i]>ans2)
                    ans2=mx2[x][i];
                }
                else if(mx1[x][i]>ans2)
                ans2=mx1[x][i];
                if(mx1[y][i]>ans1)
                {
                    ans2=ans1,ans1=mx1[y][i];
                    if(mx2[y][i]>ans2)
                    ans2=mx2[y][i];
                }
                else if(mx1[y][i]>ans2)
                ans2=mx1[y][i];
                x=f[x][i];
                y=f[y][i];
            }
        }
        if(mx1[x][0]>ans1)
        {
            ans2=ans1,ans1=mx1[x][0];
            if(mx2[x][0]>ans2)
            ans2=mx2[x][0];
        }
        else if(mx1[x][0]>ans2)
        ans2=mx1[x][0];
        if(mx1[y][0]>ans1)
        {
            ans2=ans1,ans1=mx1[y][0];
            if(mx2[y][0]>ans2)
            ans2=mx2[y][0];
        }
        else if(mx1[y][0]>ans2)
        ans2=mx1[y][0];
        //cout<<ans1<<' '<<ans2<<endl;
        if(ans1!=ff[kk].z)
        anss=min(anss,ff[kk].z-ans1);
        else
        anss=min(anss,ff[kk].z-ans2);
    }
    int main()
    {	
        ll cnt=0;
        cin>>n>>m;
        for(ll i=1;i<=n;i++)
        fa[i]=i;
        for(ll i=1;i<=m;i++)
        scanf("%lld%lld%lld",&ff[i].x,&ff[i].y,&ff[i].z);
        sort(ff+1,ff+1+m);
        for(ll i=1;i<=m;i++)
        {
            ll x=ff[i].x,y=ff[i].y;
            ll f1=find(x),f2=find(y);
            if(f1!=f2)
            {	
                vis[i]=1;
                cnt++;
                fa[f1]=f2;
                ans+=ff[i].z;
                add(x,y,ff[i].z);
                add(y,x,ff[i].z);
            }
            if(cnt==n-1) break;
        }
        dep[1]=1;
        dfs(1,0);
        zuxian();
        for(ll i=1;i<=m;i++)
        {
            if(vis[i]) continue;
            ll x=ff[i].x,y=ff[i].y;
            lca(x,y,i);
        }
        cout<<ans+anss;
    }
    

    博主蒟蒻,可以随意转载,但必须附上原文链接k-z-j

  • 相关阅读:
    Eclipse debug模式下使用16进制(Hex)查看变量值
    无线局域网中RADIUS协议原理与实现
    浏览器发送URL的编码特性
    跨域共享cookie和跨域共享session
    Nginx与Apache工作方式
    Http字段含义
    http中有关缓存相关的几个字段
    maven中用yuicompressor和closure-compiler对js、css文件进行压缩
    Mysql 忘记密码----修改Navicat的连接密码--以及--(加入安装Navicat时没设置密码)有时新建连接设置密码,连接不成功---的问题解决方法 密码忘记的解决
    RedisTemplate的各种操作(set、hash、list、string)
  • 原文地址:https://www.cnblogs.com/kzj-pwq/p/9493568.html
Copyright © 2011-2022 走看看