AcWing 356 次小生成树(LCA+并查集)

Posted CCSU_Cola

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AcWing 356 次小生成树(LCA+并查集)相关的知识,希望对你有一定的参考价值。

链接: [AcWing 356. 次小生成树 - AcWing]()

思路: 先并查集建图,构造最小生成树,次小生成树即为在最小生成树上加一条边构成一个环,然后去掉原图中最大的边即可,但此处最小生成树为严格次小,所以除了两个点之间的最大边权,还需维护一个次大边权,否则替换的边权可能和最大边权相等。可以在bfs处理LCA的过程中维护出来,然后后续加边时,查找两点之间的最大和次大边权,判断是否能够替换即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct tt{
    int x,y,val,id;
};
tt p[300010];
int n,m;
bool cmp(tt a,tt b){
    return a.val<b.val;
}
int ff[100010];
int find(int x){
    if(ff[x]==x)return x;
    return ff[x]=find(ff[x]);
}
int d1[100010][20],d2[100010][20];
vector<pair<int,int> >w[100010];
int depth[100010],fa[100010][20];
void bfs(int root){
    memset(depth,0x3f,sizeof depth);
    depth[0]=0,depth[root]=1;
    queue<int>q;
    q.push(root);
    while(!q.empty()){
        int t=q.front();
        q.pop();
        int k=w[t].size();
        for(int i=0;i<k;i++){
            int v=w[t][i].first;
            if(depth[v]>depth[t]+1){
                depth[v]=depth[t]+1;
                q.push(v);
                fa[v][0]=t;
                d1[v][0]=w[t][i].second;
                d2[v][0]=-0x7fffffff;
                for(int k=1;k<=16;k++){
                    int ff=fa[v][k-1];
                    fa[v][k]=fa[ff][k-1];
                    int dist[5];
                    dist[1]=d1[v][k-1];//v位置向上2的k-1次方个距离中最大路径
                    dist[2]=d2[v][k-1];//v位置向上2的k-1次方个距离中次大路径
                    dist[3]=d1[ff][k-1];//ff位置向上2的k-1次方个距离中最大路径
                    dist[4]=d2[ff][k-1];//ff位置向上2的k-1次方个距离中次大路径
                    d1[v][k]=d2[v][k]=-0x7fffffff;
                    for(int i=1;i<=4;i++){
                        if(dist[i]>d1[v][k]){
                            d2[v][k]=d1[v][k];
                            d1[v][k]=dist[i];
                        }
                        else if(dist[i]!=d1[v][k]&&dist[i]>d2[v][k]){
                            d2[v][k]=dist[i];
                        }
                    }
                }
            }
        }
    }
}
int LCA(int a,int b,int w){
    int dist[200010];//所有找公共父亲过程中的最大和次大距离全部存在dist数组中
    int cnn=1;
    if(depth[a]<depth[b])swap(a,b);
    for(int k=16;k>=0;k--){
        if(depth[fa[a][k]]>=depth[b]){
            dist[cnn++]=d1[a][k];
            dist[cnn++]=d2[a][k];
            a=fa[a][k];
        }
    }
    if(a!=b){
    for(int k=16;k>=0;k--){
        if(fa[a][k]!=fa[b][k]){
            dist[cnn++]=d1[a][k];
            dist[cnn++]=d2[a][k];
            dist[cnn++]=d1[b][k];
            dist[cnn++]=d2[b][k];
            a=fa[a][k];
            b=fa[b][k];
        }
    }
    dist[cnn++]=d1[a][0];
    dist[cnn++]=d1[b][0];
    }
    int mx=-0x7fffffff,mx1=-0x7fffffff;
    for(int i=1;i<cnn;i++){
        if(dist[i]>mx){
            mx1=mx,mx=dist[i];
        }
        else if(dist[i]!=mx&&dist[i]>mx1){
            mx1=dist[i];
        }
    }
    if(w>mx)return mx;
    if(w>mx1)return mx1;
    return 0x7fffffff;
}
ll ans=0;
int main(){
    scanf("%d%d",&n,&m);
    int a,b,c;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&c);
        p[i].x=a,p[i].y=b,p[i].val=c;
    }
    sort(p+1,p+1+m,cmp);
    for(int i=1;i<=n;i++){
        ff[i]=i;
    }
    int cnt=0;
    for(int i=1;i<=m;i++){
        int xx=find(p[i].x);
        int yy=find(p[i].y);
        if(xx!=yy){
            ff[xx]=yy;
            w[p[i].x].push_back({p[i].y,p[i].val});
            w[p[i].y].push_back({p[i].x,p[i].val});
            ans+=p[i].val;
            p[i].id=1;
            cnt++;
        }
        if(cnt==n-1)break;
    }
    bfs(1);
    ll res=1e18;
    for(int i=1;i<=m;i++){
        if(!p[i].id){
            int xx=p[i].x,yy=p[i].y;
            res=min(res,ans-LCA(xx,yy,p[i].val)+p[i].val);
        }
    }
    printf("%lld\\n",res);
    return 0;
}

以上是关于AcWing 356 次小生成树(LCA+并查集)的主要内容,如果未能解决你的问题,请参考以下文章

次小生成树

「LuoguP4180」 模板严格次小生成树[BJWC2010](倍增 LCA Kruscal

AcWing1148 秘密的奶牛运输(次小生成树)

AcWing1148 秘密的奶牛运输(次小生成树)

次小生成树(LCA倍增)

洛谷.4180.[模板]次小生成树Tree(Kruskal LCA 倍增)