嚴格次小生成樹[BJWC2010]


原文必點
原題鏈接

題目描述

給定一張\(N\) 個點$ M $條邊的無向圖,求無向圖的嚴格次小生成樹。

設最小生成樹的邊權之和為\(sum\),嚴格次小生成樹就是指邊權之和大於\(sum\)的生成樹中最小的一個。

輸入格式

第一行包含兩個整數\(N\)\(M\)

接下來\(M\)行,每行包含三個整數\(x,y,z\),表示點\(x\)和點\(y\)之前存在一條邊,邊的權值為\(z\)

輸出格式

包含一行,僅一個數,表示嚴格次小生成樹的邊權和。(數據保證必定存在嚴格次小生成樹)

數據范圍

\[N \le 10^5 \\\\ M \le 3*10^5 \]

輸入樣例:

5 6 
1 2 1 
1 3 2 
2 4 3 
3 5 4 
3 4 3 
4 5 6 

輸出樣例:

11

解題報告

題意理解

要你構造一棵\(n\)個節點的嚴格次小生成樹.

算法解析

分析條件

題目中給出的關鍵點,就是嚴格和次小.

  1. 什么是嚴格

就是題目強制要求嚴格單調性,不可以有\(=\)號的出現.

  1. 什么是次小

我們應該都知道,最小生成樹,它要求邊集合的邊總和最小,那么次小生成樹,要求邊集合的邊總和只比最小生成樹邊集合權值大.

總結性質

有至少一個(嚴格)次小生成樹,和最小生成樹之間只有一條邊的差異。和真理只有一點差異,那就是出題人毒瘤

我們來粗略證明一下.(強行偽證)

我們知道最小生成樹,是由\(n-1\)條構成的.

那么其他的\(M-N+1\)就是多余邊.

假如說我們把一條多余邊\((x,y,z)\),加入到了最小生成樹中,那么一定會在\((x,y)\)之間的路徑上形成一個環.

那么這個環上面,最大的邊稱之為

\[Val_1 \]

次大的邊,稱之為

\[Val_2 \]

而且為了保證嚴格這個單調性質,我們必須

\[Val_1>Val_2 \quad 最大的邊一定大於次大的邊 \]

接下來,我們就需要好好分析一下這條多余邊了.

我們知道多余邊,替換任何一條樹上的一條邊,都會使得最小生成樹,不再最小

為什么?

因為最小生成樹上的每一條邊,一定是滿足貪心性質下的最小的邊.為什么啊?相信你的直覺啊

這個證明,我們使用的克魯斯卡爾算法,已經告訴我們為什么.真相只有一個,我懶了

總而言之,言而總之,我們現在知道了這條多余邊的加入.,一定會產生非最小生成樹.

我們不妨令

\[ans=最小生成樹邊權之和 \]

假如說我們將多余邊,替換掉最大權值邊.

\[Val_1 ==> z \\\ 此時我們發現當前生成樹 W=ans+z-Val_1 \\\\ W=最小生成邊權之和+加上多余邊-最大權值邊 \]

這一輪替換,我們可以認為這棵生成樹有潛力成為次小生成樹.

然后,我們發現,換一換次大邊,也是可以的.

我們將多余邊,強行替換掉次大權值邊.

\[Val_2 ==> z \\\\ 此時當前生成樹 W=ans+z-Val_2 \\\\ W=最小生成樹之和+加入多余邊-次大權值邊 \]

現在所有的候選生成樹都出來了,但是我們面臨一個非常嚴重的問題.

我們如何快速計算,一條路徑上的最大邊,和次大邊.


動態規划

我們可以當前需要知道的狀態,無非就是兩個.

  1. 一條路徑上的最大邊
  2. 一條路徑上的嚴格次大邊

所以說,我們不妨就按照倍增數組的思路,去制造兩個新數組.

  1. 最大邊數組
  2. 嚴格次大邊數組

\[f[x][k]=f[fa[x][k-1]][k-1] \]

這是我們非常熟悉的Lca倍增數組.

然后咱們現在其實,手上掌握的最有力的性質,就是最值性質.

我們假設一條路徑是由三段構造而成.

是三段,不是就三個點.

\[a=>c,c=>b,b=>a \]

次小生成樹.png

我們發現

\[A=>B的最大值其實等於 \\\\ max(A=>C最大值,B=>C最大值) \]

這就是區間最值性質.

不過嚴格次大邊,就比較麻煩了,不慌,咱們慢慢畫圖來.

為了下面簡述方面,我們設置一下變量.

\[A=>C上最大邊權為Val_{A,C} \quad 次大邊權為V_{A,C} \\\\ C=>B上最大邊權為Val_{B,C} \quad 次大邊權為V_{B,C} \\\\ A=>B上最大邊權為Val_{A,B} \quad 次大邊權為V_{A,B} \\\\ \]

巧計一下,Val字母多,所以是最大邊權,V字母少,所以是次大邊權.

我們分類討論一下,三種情況.

①第一段最大值=第二段最大值

\[Val_{A,C}=Val_{B,C} \]

我們發現兩段居然最大值一樣.

次大邊權就只能

\[V_{A,B}=max(V_{A,C},V_{B,C}) \]

②第一段最大值<第二段最大值.

那么此時,次大邊權可以取第一段最大值.

因為此時總段的最大值,一定是第二段最大值.

\[Val_{A,B}=Val_{B,C} \\\\ 因此V_{A,B}可以=Val_{A,C} \]

綜上所述,我們總結下來就是.

\[V_{A,B}=max(Val_{A,C},V_{B,C}) \]

③第一段最大值>第二段最大值.

那么此時,次大邊權是可以取第二段最大值.

因為此時總段的最大值,一定是第一段最大值.

\[Val_{A,B}=Val_{A,C} \\\\ 因此V_{A,B}可以=Val_{B,C} \]

同樣,總結一下.

\[V_{A,B}=max(Val_{B,C},v_{A,B}) \]

然后我們\(A,B,C\)具體化一下.

A其實就是起始節點.

C其實就是A跳躍了\(2^{i-1}\)格節點.

B其實就是A跳躍了\(2^{i}\)格節點.

廣告時間:發現還是有點模糊,咱們的直播課會講解的非常清晰,畫圖肯定少不了.


代碼解析

#include <bits/stdc++.h>
using namespace std;
#define INF 1e16
const int N=1e5+200;
const int M=6*1e5+300;
int head[M],edge[M],Next[M],ver[M],tot,fa[M],n,m,father[N][32],deep[N];
long long dp[2][N][32],val1,val2,ans_max,ans;
struct node
{
    int x,y,z,vis;
} s[M];
int cmp(node a,node b)
{
    return a.z<b.z;
}
struct Edge
{
    void init2()
    {
        memset(head,0,sizeof(head));
        tot=0;
    }
    void add_edge(int a,int b,int c)
    {
        edge[++tot]=b;
        ver[tot]=c;
        Next[tot]=head[a];
        head[a]=tot;
    }
    int find(int x)
    {
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    void Kruskal()
    {
        sort(s+1,s+1+m,cmp);
        for(int i=1; i<=m; i++)
        {
            int a=find(s[i].x),b=find(s[i].y);
            if (a==b)
                continue;
            s[i].vis=1;
            fa[a]=b;
            ans+=s[i].z;
            add_edge(s[i].x,s[i].y,s[i].z);
            add_edge(s[i].y,s[i].x,s[i].z);
        }
    }
    void bfs(int root)
    {
        deep[root]=0;
        queue<int> q;
        q.push(root);
        while(q.size())
        {
            int x=q.front(),len=(int)log2(deep[x]+1);
            q.pop();
            for(int i=head[x]; i; i=Next[i])
            {
                int y=edge[i];
                if(y==father[x][0])
                    continue;
                deep[y]=deep[x]+1;
                father[y][0]=x,dp[0][y][0]=ver[i],dp[1][y][0]=-INF;
                q.push(y);
                for(int t=1; t<=len; t++)
                {
                    father[y][t]=father[father[y][t-1]][t-1];
                    if(dp[0][y][t-1]!=dp[0][father[y][t-1]][t-1])
                    {
                        dp[0][y][t]=max(dp[0][y][t-1],dp[0][father[y][t-1]][t-1]);
                        dp[1][y][t]=min(dp[0][y][t-1],dp[0][father[y][t-1]][t-1]);
                    }
                    else
                    {
                        dp[0][y][t]=dp[0][y][t-1];
                        dp[1][y][t]=max(dp[1][y][t-1],dp[1][father[y][t-1]][t-1]);
                    }
                }
            }
        }
    }
    inline void update2(int x)
    {
        if(x>val1)
            val2=val1,val1=x;
        else if(x>val2 && x!=val1)
            val2=x;
    }
    inline void update(int x, int t)
    {
        update2(dp[0][x][t]);
        update2(dp[1][x][t]);
    }
    inline void Lca(int x, int y)
    {
        val1=val2=-INF;
        if(deep[x]<deep[y])
            swap(x,y);
        while(deep[x]>deep[y])
        {
            int t=(int)log2(deep[x]-deep[y]);
            update(x,t),x=father[x][t];
        }
        if(x==y)
            return;
        for(int t=(int)log2(deep[x]); t>=0; t--)
        {
            if(father[x][t]!=father[y][t])
            {
                update(x,t),update(y,t);
                x=father[x][t];
                y=father[y][t];
            }
        }
        update(x,0),update(y,0);
    }
} g1;
int main()
{
//	freopen("stdin.in","r",stdin);
//	freopen("stdout.out","w",stdout);
    scanf("%d%d",&n,&m);
    g1.init2();
    for(int i=1; i<=m; i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        s[i].x=a,s[i].y=b,s[i].z=c;
        fa[i]=i;
    }
    g1.Kruskal();
    g1.bfs(1);
    ans_max=INF;
    for(int i=1; i<=m; i++)
    {
        if(!s[i].vis)
        {
            g1.Lca(s[i].x,s[i].y);
            if(val1!=s[i].z)
                ans_max=min(ans_max,ans-val1+s[i].z);
            else
                ans_max=min(ans_max,ans-val2+s[i].z);
        }
    }
    printf("%lld\n",ans_max);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM