次小生成树
那肯定是最小生成树上改一条边(改两条肯定不如只改其中一条小)
那就枚举所有不在最小生成树上的边
考虑如果把此边加入,另一边删除后的情况
考虑要删哪条边后才能保持树的形态,并且总长最小
加入一条边后树就会出现一个环
那么删掉的边要在加入的边连接的两点间的路径上
并且删去的边要尽量大
那就可以用LCA来求出树上两点间路径上的最长边
但是现在还有一个问题,可能删去的边和加入的边一样长
所以还要维护一下次长的边
次长边维护也不难,具体看代码
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<vector> using namespace std; inline int read() { int x=0; char ch=getchar(); while(ch<‘0‘||ch>‘9‘) ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x; } const int N=1e5+7; const int M=3e5+7; int n,m; int fa[N]; long long s; inline int find(int x){ return fa[x]==x ? x : fa[x]=find(fa[x]); } struct edge { int a,b,c; }e[M]; inline bool cmp(const edge &a,const edge &b){ return a.c<b.c; } vector <int> v[N],g[N]; bool p[M]; int f[N][27],dep[N]; long long mx[N][27],mxx[N][27];//mxx存次长边 void dfs(int x,int father) { dep[x]=dep[father]+1; f[x][0]=father; for(int i=1;i<=20;i++) { f[x][i]=f[f[x][i-1]][i-1]; mx[x][i]=max(mx[f[x][i-1]][i-1],mx[x][i-1]); mxx[x][i]=max(mxx[f[x][i-1]][i-1],mxx[x][i-1]); if(mx[ f[x][i-1] ][i-1]>mx[x][i-1]) mxx[x][i]=max(mxx[x][i],mx[x][i-1]); if(mx[x][i-1]>mx[ f[x][i-1] ][i-1]) mxx[x][i]=max(mxx[x][i],mx[ f[x][i-1] ][i-1]); } int len=v[x].size(); for(int i=0;i<len;i++) { int u=v[x][i]; if(u==father) continue; mx[u][0]=g[x][i]; mxx[u][0]=-1; dfs(u,x); } } inline long long LCA(int x,int y,int z)//询问最长边或者次长边 { long long res=-1e17+7; if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--) if(dep[f[x][i]]>=dep[y]) { if(mx[x][i]!=z) res=max(res,mx[x][i]); else res=max(res,mxx[x][i]); x=f[x][i]; } if(x==y) return res; for(int i=20;i>=0;i--) if(f[x][i]!=f[y][i]) { if(mx[x][i]!=z) res=max(res,mx[x][i]); else res=max(res,mxx[x][i]); if(mx[y][i]!=z) res=max(res,mx[y][i]); else res=max(res,mxx[y][i]); x=f[x][i]; y=f[y][i]; } if(mx[x][0]!=z) res=max(res,mx[x][0]); else res=max(res,mxx[x][0]); if(mx[y][0]!=z) res=max(res,mx[y][0]); else res=max(res,mxx[y][0]); return res; } int main() { n=read(); m=read(); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) e[i].a=read(),e[i].b=read(),e[i].c=read(); sort(e+1,e+m+1,cmp); int tot=0; for(int i=1;i<=m;i++) { int xa=find(e[i].a),xb=find(e[i].b); if(xa==xb) continue; s+=e[i].c; fa[xa]=xb; p[i]=1; v[e[i].a].push_back(e[i].b); g[e[i].a].push_back(e[i].c); v[e[i].b].push_back(e[i].a); g[e[i].b].push_back(e[i].c); if(++tot==n-1) break; }//先求出最小生成树 dfs(1,1); long long ans=1e17+7; for(int i=1;i<=m;i++) { if(p[i]) continue;//枚举所有没有加入最小生成树的边 long long t=LCA(e[i].a,e[i].b,e[i].c);//找出最长边或者次长边 if(t==e[i].c) continue; ans=min(ans,s-t+e[i].c); } cout<<ans; return 0; }
原文地址:https://www.cnblogs.com/LLTYYC/p/9695527.html
时间: 2024-10-10 01:04:41