传送门
之前写过一次,但是理解不深刻,复习之后有了更加细节的一些理解
好了进入正题
首先,我们需要知道次小生成树一定是在最小生成树的邻集中,即次小生成树与最小生成树只会有一条边的差别
所以我们会想枚举所有非树边,看看哪条换进去可以得到次小生成树
而非树边去替换哪条树边又是一个问题,但是如果你注意到当前这条非树边与树上的一些树边构成了环——其实也就是这条非树边的两端点的LCA与之成环了
意识到这一点,问题就会简单许多,我们可以利用LCA中的倍增思想,维护路径上的权值最大边
事实上,我们还需要维护路径上的权值次大边——因为要求严格最小,不允许权值和相等(否则怎么叫次小呢),而如果权值最大边与我们枚举到的当前非树边全职相等,那就替换了个寂寞~。于是我们需要维护严格次大,如果没有次大那就是没有,一定要保证严格性
#include<bits/stdc++.h>
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
int register data=0;static char ch=0;ch=nc();
while(!isdigit(ch))ch=nc();
while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=nc();
return data;
}
const int N=1e5+1,M=3e5+1,inf=1e9;
int cnt,first[N],n,m,fa[N],dep[N],fir[N][18],sec[N][18],f[N][18],mnm;
long long sum;
bool vis[M];
struct edge{int v,w,nxt;}e[M<<1];
struct Edge{int u,v,w;}E[M];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline bool cmp(Edge x,Edge y){return x.w<y.w;}
inline void add(int u,int v,int w){e[++cnt]=(edge){v,w,first[u]},first[u]=cnt;}
inline void dfs(int u){
for(int register i=1;(1<<i)<=dep[u];++i){
f[u][i]=f[f[u][i-1]][i-1],fir[u][i]=max(fir[f[u][i-1]][i-1],fir[u][i-1]);
if(fir[f[u][i-1]][i-1]!=fir[u][i-1])sec[u][i]=min(fir[f[u][i-1]][i-1],fir[u][i-1]);
sec[u][i]=max(sec[u][i],max(sec[f[u][i-1]][i-1],sec[u][i-1]));
}
for(int register v,i=first[u];i;i=e[i].nxt){
v=e[i].v;
if(dep[v])continue;
dep[v]=dep[u]+1,fir[v][0]=e[i].w,f[v][0]=u,dfs(v);
}
}
typedef pair<int,int> T;
inline T lca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
int register gap=dep[a]-dep[b],_1=0,_2=0;
for(int register i=0;(1<<i)<=gap;++i)
if(gap&(1<<i)){
if(_1!=fir[a][i])_2=max(_2,min(_1,fir[a][i]));
_1=max(_1,fir[a][i]),_2=max(_2,sec[a][i]),a=f[a][i];
}
if(a==b)return make_pair(_1,_2);
for(int register i=17;i>=0;--i)
if(f[a][i]!=f[b][i]){
if(_1!=fir[a][i])_2=max(_2,min(_1,fir[a][i]));
_1=max(_1,fir[a][i]),_2=max(_2,sec[a][i]),a=f[a][i];
if(_1!=fir[b][i])_2=max(_2,min(_1,fir[b][i]));
_1=max(_1,fir[b][i]),_2=max(_2,sec[b][i]),b=f[b][i];
}
if(_1!=fir[a][0])_2=max(_2,min(_1,fir[a][0]));
if(_1!=fir[b][0])_2=max(_2,min(_1,fir[b][0]));
_1=max(_1,max(fir[a][0],fir[b][0])),_2=max(_2,max(sec[a][0],sec[b][0]));
return make_pair(_1,_2);
}
signed main(){
mnm=inf,n=rd(),m=rd();
for(int register i=1;i<=m;++i)E[i].u=rd(),E[i].v=rd(),E[i].w=rd();
sort(E+1,E+m+1,cmp);
for(int register i=1;i<=n;++i)fa[i]=i;
for(int register u,v,w,fu,fv,i=1;i<=m;++i){
u=E[i].u,v=E[i].v,w=E[i].w,fu=find(u),fv=find(v);
if(fu!=fv)sum+=w,vis[i]=1,fa[fu]=fv,add(u,v,w),add(v,u,w);
}dep[1]=1,dfs(1);
for(int register i=1;i<=m;++i){
if(vis[i])continue;
int register u=E[i].u,v=E[i].v,w=E[i].w;
T register t=lca(u,v);
if(w!=t.first)mnm=min(mnm,w-t.first);
else mnm=min(mnm,w-t.second);
}cout<<sum+mnm,exit(0);
}
upd:
本来可以进水谷第一页的(即使不加fread),万万没想到有一个人疯狂提交AC代码,强行把我挤出了第一页
