【题解】严格次小生成树

题目链接

题目大意:求一个严格的次小生成树,使得其边权和严格小于最小生成树。

(本来想着\(LCT\)但笔者太菜)

我们可以试着想一个暴力思路:枚举每一条加进去的边,看看替换怎么样,记录下每一次替换后的生成树边权和,然后比较答案。

先跑一边最小生成树。

考虑我们替掉树上的哪一条边。

因为我们加进去的应该是那条边所在环上的最大的边,我们替的话应该\(instead\) \(of\) 最大的边。

树上维护最大边权,想到了啥?

LCT

树上倍增。

我们可以倍增思路,预处理出来点\(i\)到它的\(2^{j}\)次方父亲的路径中的最大值。

那么我们找树上两点最大值的时间复杂度就变成了\(\log(n)\)级别。

那么我们的代码复杂度也就是约\(O(n\log n)\)

可以通过本题。

注意:连边的时候是两次,所以排序的时候要排\((m+m)\)条边,倍增处理的时候\((1<<31)\)会爆\(int\),以及注意初始\(dfs\)的传参……

最大值递推式:

\(fm[i][j]=max(fm[i][j-1],fm[fa[i][j-1]][j-1])\)

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<1)+(s<<3)+(ch^48);
        ch=getchar();
    }
    return s*w;
}
const int MAXN=5e5+10;
const int inf=(1LL<<60);
int H[MAXN<<1],tot,head[MAXN],cnt;
int n,m,fa[MAXN][31],fm[MAXN][31];
int f[MAXN],vis[MAXN],MAY,sum,ans;
int dep[MAXN];
struct edge{
    int nxt,to,dis,pre;
}g[MAXN<<1],e[MAXN];
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
inline void add(int x,int y,int dis){
    e[++tot].to=y;
    e[tot].nxt=head[x];
    e[tot].dis=dis;
    e[tot].pre=x;
    head[x]=tot;
}
inline void kdd(int x,int y,int dis){
    g[++cnt].to=y;
    g[cnt].nxt=H[x];
    g[cnt].dis=dis;
    g[cnt].pre=x;
    H[x]=cnt;
}
inline bool cmp(edge a,edge b){return a.dis<b.dis;}
void kruskal(){
    sort(g+1,g+m+m+1,cmp);
    for(int i=1;i<=2*m;++i){
        int x=g[i].pre,y=g[i].to;
        int fx=find(x),fy=find(y);
        if(fx==fy)continue;add(y,x,g[i].dis);sum+=g[i].dis;
        f[fx]=fy;add(x,y,g[i].dis);vis[i]=1;vis[i+1]=1;
    }
}
void dfs(int x,int father,int L){
    fa[x][0]=father;
    fm[x][0]=L;
    dep[x]=dep[father]+1;
    for(int i=1;(1<<i)<=dep[x];++i){
        fa[x][i]=fa[fa[x][i-1]][i-1];
        fm[x][i]=max(fm[x][i-1],fm[fa[x][i-1]][i-1]);
    }
    for(int i=head[x];i;i=e[i].nxt){
        int j=e[i].to;
        if(j==father)continue;
        dfs(j,x,e[i].dis);
    }
}
void LCA(int x,int y,int &SS,int val){
    int res=-1e18;
    if(dep[x]<dep[y])swap(x,y);
    int X=x,Y=y;
    int cha=dep[x]-dep[y];
    for(int i=30;i>=0;--i)
        if(cha>=(1<<i)){
            cha-=(1<<i);
            if(fm[x][i]!=val)res=max(res,fm[x][i]);
            x=fa[x][i];
        }   
    if(x==y){
        SS-=res;SS+=val;
        return;
    }
    for(int i=30;i>=0;--i)
        if(fa[x][i]!=fa[y][i]){
            if(fm[x][i]!=val)res=max(res,fm[x][i]);
            if(fm[y][i]!=val)res=max(res,fm[y][i]);
            x=fa[x][i],y=fa[y][i];
        }       
    if(fm[x][0]!=val)res=max(res,fm[x][0]);
    if(fm[y][0]!=val)res=max(res,fm[y][0]);
    SS-=res;SS+=val;
}
void solve(){
    ans=inf;
    int s=0;
    for(int i=1;i<=m+m;++i){
        if(!vis[i]){
            int x=g[i].pre,y=g[i].to;
            LCA(x,y,(s=sum),g[i].dis);
            ans=min(ans,s);
            vis[i+1]=1;
        }
    }
    printf("%lld\n",ans);
}
signed main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i)f[i]=i;
    for(int i=1;i<=m;++i){
        int x,y,z;
        x=read(),y=read(),z=read();
        kdd(x,y,z);kdd(y,x,z);
    } 
    kruskal();
    dfs(1,0,0);
    solve();
    return 0;
}

注意开\(long\) \(long\).

\(Over.\)

上一篇:创业学习-《内容消费行业报告》


下一篇:从模型到应用,一文读懂因子分解机