传送门: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 ",ans+delta); return 0; }