次小生成樹


傳送門

最小生成樹很好求,那么對於次小生成樹要怎么求呢?

稍加思考,我們可以想到,次小生成樹與最小生成樹差的只是一條邊

為什么呢?我們先建出一棵最小生成樹,滿足使用的邊都是最小的,剩下的邊(稱為非樹邊)一定沒有樹邊優。如果我們加入一條非樹邊,刪除最小生成樹中的一條邊,次小生成樹一定是包括在以這種方法建出的樹中的(倘若刪兩條樹邊加兩條非樹邊,則肯定沒有刪一條加一條優,絕不是次小生成樹)

於是,我們可以這樣操作:

對於每一條非樹邊,其邊的兩端點為x,y。將其加入到最小生成樹中,則一定會構成一個環

如果這時刪除這個環中的最大的邊(當然不能是插入的邊)就構成了必須加入紅色邊的最小生成樹。

注意:一定是刪除環中最大的邊,這樣才保證新生成的樹與原來的樹相差最小,才可能為次小生成樹。

刪去--->

仍然是一棵樹,且是必須加入紅色邊的最小生成樹,則有可能是次小生成樹

現在差不多已經明白了思想,就要看怎么實現了。得知了紅色邊的端點x,y,我們就可以找到它所在的環,觀察圖,發現從x到lca,再到y就構成了它所在的環。於是實現的難點就在於怎么快速的找到某個環內最大的邊。

也就是說,尋找從x到y這條路徑上的最大邊(還沒加入紅色邊呢,還沒成環,因此看路徑)

問題轉化成:尋找樹上某條路徑上的最大邊。

這樣實現就比較好想了,我們可以用樹上倍增處理處樹上路徑的最大邊:maxx[x][i]=max(maxx[f[x][i-1]]][i-1],maxx[x][i-1]),maxx存的是最大路徑值。

求(x,y)的路徑最大值就從x跳到lca,從y跳到lca,兩者取個max就行。

但是,你以為這樣這道題就結束了嗎?QAQ

我們再看題,發現題目是嚴格次小生成樹,也就是說,新生成的樹一定得比最小生成樹大,而不是大於等於。如果加入的紅邊的長度等於刪去的邊的長度就。。。wawawa(題中並沒有說所以邊的長度不同哦)

於是針對這種情況,我們還要再考慮考慮怎么辦。

我們可以這樣處理,對於樹上的路徑,我們不僅維護一個最大邊,還維護一個次大邊,這樣當最大邊等於紅邊時,不可以刪最大邊了,我們還可以刪去次大邊。

那么在倍增的時候注意一下維護細節就好。

for(int j=1;j<=20;++j)
        {
            if(dep[v]<(1<<j))break;//注意:如果深度小於向上走的步數就可以break掉了 
            f[v][j]=f[f[v][j-1]][j-1];//f是向上走到達的點 
            max1[v][j]=max(max1[v][j-1],max1[f[v][j-1]][j-1]);//max1是最大邊 
            if(max1[v][j-1]==max1[f[v][j-1]][j-1])
              g[v][j]=max(g[v][j-1],g[f[v][j-1]][j-1]);//g是次大邊 
            else
            {
                g[v][j]=min(max1[v][j-1],max1[f[v][j-1]][j-1]);
                g[v][j]=max(g[v][j],g[f[v][j-1]][j-1]);
                g[v][j]=max(g[v][j-1],g[v][j]);
            }
        }
維護路徑最大次大邊

放出完整代碼

#include<bits/stdc++.h>
#define INF 2100000001
#define M 300003
#define N 100003
#define LL long long
using namespace std;
int read()
{
    int f=1,x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
struct EDGE{
    int x,y,z,flagg;
}w[M];
struct edgee{
    int to,nextt,val; 
}e[M];
int tot=0,m,n,minn=INF; LL ans=0;
int f[N][22],max1[N][22],g[N][22],fa[N],head[N],dep[N];
bool cmp(const EDGE &a,const EDGE &b)
{
    return a.z<b.z;
}
int getfa(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=getfa(fa[x]);
}
void add(int a,int b,int v)
{
    tot++;
    e[tot].to=b;
    e[tot].nextt=head[a];
    e[tot].val=v;
    head[a]=tot;
}
void kruscal()
{
    int q=1;
    sort(w+1,w+m+1,cmp);
    for(int i=1;i<=n;++i)
      fa[i]=i;
    for(int i=1;i<=m;++i)
    {
        int s1=getfa(w[i].x);
        int s2=getfa(w[i].y);
        if(s1!=s2)
        {
            ans+=w[i].z;w[i].flagg=1;
            q++;
            fa[s1]=s2;
            add(w[i].x,w[i].y,w[i].z);
            add(w[i].y,w[i].x,w[i].z);
        } 
        if(q==n) break;
    }
}
void dfs(int x)
{
    for(int i=head[x];i;i=e[i].nextt)
    {
        int v=e[i].to;
        if(v==f[x][0]) continue;
        f[v][0]=x;
        max1[v][0]=e[i].val;
        dep[v]=dep[x]+1;
        for(int j=1;j<=20;++j)
        {
            if(dep[v]<(1<<j))break;//注意:如果深度小於向上走的步數就可以break掉了 
            f[v][j]=f[f[v][j-1]][j-1];//f是向上走到達的點 
            max1[v][j]=max(max1[v][j-1],max1[f[v][j-1]][j-1]);//max1是最大邊 
            if(max1[v][j-1]==max1[f[v][j-1]][j-1])
              g[v][j]=max(g[v][j-1],g[f[v][j-1]][j-1]);//g是次大邊 
            else
            {
                g[v][j]=min(max1[v][j-1],max1[f[v][j-1]][j-1]);
                g[v][j]=max(g[v][j],g[f[v][j-1]][j-1]);
                g[v][j]=max(g[v][j-1],g[v][j]);
            }
        }
        dfs(v);
    }
}
int LCA(int u,int x)
{
    if(dep[u]<dep[x])swap(u,x);
    for(int i=20;i>=0;--i)if(dep[f[u][i]]>=dep[x])u=f[u][i];
    if(x==u) return x;
    for(int i=20;i>=0;--i)if(f[x][i]!=f[u][i])x=f[x][i],u=f[u][i];
    return f[x][0];
}
void change(int x,int lca,int val)
{
    int maxx1=0,maxx2=0;
    int d=dep[x]-dep[lca];
    for(int i=0;i<=20;++i)
    {
        if(d<(1<<i))break;
        if(d&(1<<i))
        {
            if(max1[x][i]>maxx1)
            {
                maxx2=max(maxx1,g[x][i]);
                maxx1=max1[x][i];
            }
            x=f[x][i];
        }
    }
    if(val!=maxx1) minn=min(minn,val-maxx1);
    else minn=min(minn,val-maxx2);
}
void work()
{
    for(int i=1;i<=m;++i)
    {
        if(!w[i].flagg)
        {
            int s1=w[i].x,s2=w[i].y;
            int lca=LCA(s1,s2);
            change(s1,lca,w[i].z);change(s2,lca,w[i].z);
        }
    }
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;++i)
    {
        w[i].x=read();w[i].y=read();w[i].z=read();
    }
    kruscal();
//    printf(">>%d\n",ans);
    dfs(1);
    work();
    printf("%lld\n",ans+minn);
} 
/*
5 7
1 3 1
1 4 10
3 4 2
3 2 3
2 4 7
1 5 1
3 5 19
*/
嚴格次大生成樹

寫了好久終於寫完了(撒花~✿✿ヽ(°▽°)ノ✿)

 


免責聲明!

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



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