2 条题解

  • 0
    @ 2026-6-14 15:19:23

    // 最小生成树 kruskal算法 O(M*logM)
    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    
    const int N=100005,M=300005,INF=0x3f3f3f3f;
    int idx,h[N],to[M],ne[M],ww[M];
    void add(int u,int v,int w){ //连边
      to[++idx]=v,ww[idx]=w,ne[idx]=h[u],h[u]=idx;
      to[++idx]=u,ww[idx]=w,ne[idx]=h[v],h[v]=idx;
    }
    int n,m; ll sum;
    struct E{int u,v,w;}e[M]; //边集
    bool used[M];
    int fa[N]; //并查集的fa
    
    int find(int u){ //并查集的找根
      return fa[u]==u?u:fa[u]=find(fa[u]);
    }
    void kruskal(){
      int tot=0;
      for(int i=1; i<=n; i++) fa[i]=i;
      sort(e+1,e+m+1,[&](E u,E v){return u.w<v.w;});
      for(int i=1; i<=m; i++){
        int u=find(e[i].u),v=find(e[i].v);
        if(u!=v){
          fa[u]=v;
          sum+=e[i].w;  //累加边权和
          used[i]=true; //记录树边
          add(e[i].u,e[i].v,e[i].w); //建最小生成树
          if(++tot==n-1) break;
        }
      }
    }
    
    struct Tree{
      int fa[N][18],dep[N];
      int d1[N][18]; //d1[u][i]表示从u点开始向上跳2^i条边,这条路径上的最大边权
      int d2[N][18]; //d2[u][i]表示从u点开始向上跳2^i条边,这条路径上的次大边权,不存在为-INF
    
      void dfs(int u,int f){ //预处理fa,d1,d2数组
        dep[u]=dep[f]+1; fa[u][0]=f; d2[u][0]=-INF;
        for(int i=1; i<=17; i++){
          fa[u][i]=fa[fa[u][i-1]][i-1];
          
          int d[4]={d1[u][i-1],d1[fa[u][i-1]][i-1],d2[u][i-1],d2[fa[u][i-1]][i-1]};
          sort(d,d+4);
          d1[u][i]=d[3]; //最大边权
          int p=2;
          while(p>=0 && d[p]==d[3]) p--;
          d2[u][i]=(p==-1?-INF:d[p]); //次大边权
        }
        
        for(int i=h[u]; i; i=ne[i]){
          int v=to[i],w=ww[i];
          if(v!=f){
            d1[v][0]=w;
            dfs(v,u);
          }
        }
      }
      int lca(int u,int v){ //倍增求lca
        if(dep[u]<dep[v]) swap(u,v);
        for(int i=17;i>=0;i--)if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
        if(u==v) return u;
        for(int i=17; i>=0; i--)if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
        return fa[u][0];
      }
      int query(int u,int v,int w){ //倍增求小于w的最大边权
        int res=-INF;
        for(int i=17; i>=0; i--){
          if(dep[fa[u][i]]>=dep[v]){
            if(w>d1[u][i]) res=max(res,d1[u][i]);
            else if(w==d1[u][i]) res=max(res,d2[u][i]);
            u=fa[u][i];
          }
        }
        return res;
      }
    }T;
    
    int main(){
      scanf("%d%d",&n,&m);
      for(int i=1,u,v,w; i<=m; i++){
        scanf("%d%d%d",&u,&v,&w);
        e[i]={u,v,w};
      }
      kruskal();
      T.dfs(1,0);
      
      ll ans=1e18;
      for(int i=1; i<=m; i++)if(!used[i]){ //非树边
        auto [u,v,w]=e[i];
        int l=T.lca(u,v);
        ll w1=T.query(u,l,w),w2=T.query(v,l,w);
        ans=min(ans,sum-max(w1,w2)+w);
      }
      printf("%lld\n",ans);
    }
    
    • 0
      @ 2025-10-8 17:05:15
      #include <bits/stdc++.h>
      using namespace std;
      typedef long long LL;
      const int N=1e5+5,M=3e5+5;
      const int inf=0x7fffffff;
      vector<pair<int,int>> G[N];
      struct edge{int x,y,c;} e[M];int vis[M];
      bool cmp(edge n1,edge n2){return n1.c < n2.c;}
      int n,m,fa[N];LL ans;
      int findfa(int x){ return (fa[x]==x)?x:fa[x]=findfa(fa[x]);}
      void kruskal()
      {
          for(int i=1;i<=n;i++) fa[i]=i;
          sort(e+1,e+m+1,cmp);memset(vis,0,sizeof(vis));
          ans=0;
          for(int i=1,t=0;i<=m;i++)
          {
              int x=e[i].x,y=e[i].y,c=e[i].c;
              int tx=findfa(x),ty=findfa(y);
              if(tx!=ty)
              {
                  fa[tx]=ty;
                  ans=ans+c;vis[i]=1;
                  G[x].push_back({y,c}),G[y].push_back({x,c});
                  if(++t==n-1) return;
              }
          }
      }
      int D,dep[N],f[N][20],g[N][20][2];
      void dfs(int x,int fa,int c)
      {
          dep[x]=dep[fa]+1; f[x][0]=fa;
          g[x][0][0]=c;
          g[x][0][1]=-inf;
          for(int i=1;i<=D;i++)
          {
              f[x][i]=f[f[x][i-1]][i-1];
              g[x][i][0]=max(g[x][i-1][0],g[f[x][i-1]][i-1][0]);
              if(g[x][i-1][0]==g[f[x][i-1]][i-1][0]) g[x][i][1]=max(g[x][i-间][1],g[f[x][i-1]][i-1][1]);
              if(g[x][i-1][0]<g[f[x][i-1]][i-1][0]) g[x][i][1]=max(g[x][i-间][0],g[f[x][i-1]][i-1][1]);
              if(g[x][i-1][0]>g[f[x][i-1]][i-1][0]) g[x][i][1]=max(g[x][i-间][1],g[f[x][i-1]][i-1][0]);
          }
          for(auto i:G[x])
          {
              int y=i.first,c=i.second;if(y==fa) continue;
              dfs(y,x,c);
          }
      }
      int lca(int x,int y)
      {
          if(dep[x]<dep[y]) swap(x,y);
          for(int i=D;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
          if(x==y) return x;
          for(int i=D;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
          return f[x][0];
      }
      int get_max(int x,int y,int c)
      {
          int res=-inf;
          for(int i=D;i>=0;i--) if(dep[f[x][i]]>=dep[y])
          {
              if(c>g[x][i][0]) res=max(res,g[x][i][0]);
              if(c==g[x][i][0]) res=max(res,g[x][i][1]);
              x=f[x][i];
          }
          return res;
      }
      int main()
      {
          scanf("%d%d",&n,&m);
          for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].c);
          kruskal();
          dep[0]=0;D=log2(n);dfs(1,0,0);
          LL t=inf;
          for(int i=1;i<=m;i++) if(!vis[i])
          {
              int x=e[i].x,y=e[i].y,c=e[i].c;
              int p=lca(x,y);
              LL tt=max(get_max(x,p,c),get_max(y,p,c));
              t=min(t,c-tt);
          }
          printf("%lld",ans+t);return 0;
      }
      
      • 1

      D141【LCA最近公共祖先:严格次小生成树】[BJWC2010] 严格次小生成树

      信息

      ID
      3642
      时间
      1000ms
      内存
      512MiB
      难度
      10
      标签
      递交数
      7
      已通过
      4
      上传者