传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1911
思路:这题是严格次小生成树
首先考虑不严格的次小生成树
我们先求出最小生成树
然后对每条不在最小生成树的边(x,y),求出x->y路径上的最大的边,把它替换这条边之后的树就可能是次小生成树
用倍增思想记录max[x][i]表示x的第2^i的祖先到x的边上的最大值就可以做到O(nlogn)
严格次小稍微有些区别,如果路径最大边和边(x,y)权值相同,就不能替换,而要换严格次大的边
所以多记一个secmax[x][i]表示x的第2^i的祖先到x的边上的严格最大值即可
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=100010,maxm=300010,maxk=22;
using namespace std;
typedef long long ll;
int n,m,pre[maxm],now[maxn],son[maxm],val[maxm],delta=(int)1e9+7,tot;ll ans;
int fa[maxn][maxk],fim[maxn][maxk],sem[maxn][maxk],dep[maxn],f[maxn];bool in[maxm];
struct Edge{int x,y,v;}E[maxm];
bool operator <(Edge a,Edge b){return a.v<b.v;}
void add(int a,int b,int c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;}
int getfa(int x){return f[x]==x?x:f[x]=getfa(f[x]);}
void dfs(int x){
for (int i=1;i<=18;i++){
fa[x][i]=fa[fa[x][i-1]][i-1];
int t1=fim[x][i-1],t2=fim[fa[x][i-1]][i-1];
fim[x][i]=max(t1,t2);
sem[x][i]=max(sem[x][i-1],sem[fa[x][i-1]][i-1]);
if (t1!=t2) sem[x][i]=max(sem[x][i],min(t1,t2));
}
for (int y=now[x];y;y=pre[y]) if (son[y]!=fa[x][0]){
dep[son[y]]=dep[x]+1,fa[son[y]][0]=x;
fim[son[y]][0]=val[y],dfs(son[y]);
}
}
void kruskal(){
sort(E+1,E+1+m);int cnt=0;
for (int i=1;i<=n;i++) f[i]=i;
for (int i=1;i<=m&&cnt<n-1;i++){
int x=E[i].x,y=E[i].y,v=E[i].v;
if (getfa(x)==getfa(y)) continue;
cnt++,f[getfa(x)]=getfa(y),ans+=v,in[i]=1;
add(x,y,v),add(y,x,v);
}
}
int lca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
for (int h=dep[x]-dep[y],i=18;i>=0&&h;i--) if (h&(1<<i)) x=fa[x][i];
if (x==y) return x;
for (int i=18;i>=0;i--)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void query(int x,int u,int v){
int max1=0,max2=0;
for (int i=18,h=dep[x]-dep[u];i>=0;i--) if (h&(1<<i)){
if (fim[x][i]>max1) max2=max1,max1=fim[x][i];
max2=max(max2,sem[x][i]),h-=(1<<i);
}
if (v==max1) delta=min(delta,v-max2);
else delta=min(delta,v-max1);
}
void solve(int id){
int x=E[id].x,y=E[id].y,v=E[id].v,u=lca(x,y);
query(x,u,v),query(y,u,v);
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1,x,y,z;i<=m;i++) scanf("%d%d%d",&x,&y,&z),E[i]=(Edge){x,y,z};
kruskal(),dfs(1);
for (int i=1;i<=m;i++) if (!in[i]) solve(i);
printf("%lld\n",ans+delta);
return 0;
}