Description
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
Input
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
Output
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
首先呢有意思的是严格次小生成树与非严格次小生成树都是只与最小生成树只有一条边的差距,证明方法与非严格次小生成树的证明相似。
知道了这个结论了求严格次小生成树就比较简单了,如求次短路一样,处理maxx[i][26],maxx2[i][26],两个数组代表点i向上2的k次方步的最大与严格次大值,如果最大由两个不一样的最大递推而来,那么次大可选两个最大的小的那一个,也可以是由两个次大的大者递推而来。
在处理lca的时候,如果最大值被更新了,次大值就会是当前次大值与之前的最大值的最大值,如果最大值没有被当前的maxx[i][k]更新,那么次大值也有可能是maxx[i][k]与当前次大值的较大者,当然次大值本身也是当前向上k次方步的次大值与原来次大值的较大者。
下附AC代码。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define maxn 300005
using namespace std;
typedef long long ll;
ll n,m;
ll cnt,tot;
ll vis[maxn],dep[maxn];
ll anc[maxn][26],maxx[maxn][26],maxx2[maxn][26];
ll head[2*maxn],ne[2*maxn],to[2*maxn],val[2*maxn];
void add(ll x,ll y,ll w)
{
to[++tot]=y;val[tot]=w;ne[tot]=head[x];head[x]=tot;
}
//以下为最小生成树
ll fa[maxn];
ll u[maxn],e[maxn],w[maxn],temp[maxn];
ll cmp(ll i,ll j)
{
return w[i]<w[j];
}
ll getfa(ll now)
{
return fa[now]==now ? now : fa[now]=getfa(fa[now]);
}
ll kruskal()
{
sort(temp+1,temp+1+m,cmp);
for(ll i=1;i<=n;i++)
fa[i]=i;
ll ans=0;
for(ll i=1;i<=m;i++)
{
ll now=temp[i];
ll x1=getfa(u[now]);
ll y1=getfa(e[now]);
if(x1!=y1)
{
vis[temp[i]]=1;
fa[x1]=y1;
add(e[now],u[now],w[now]);
add(u[now],e[now],w[now]);
ans+=w[now];
cnt++;
}
}
return cnt==n-1 ? ans : -1;
}
//以下为处理lca
void dfs(ll now,ll pa)
{
for(ll i=1;i<=24;i++)
{
if((1<<i)>dep[now]) break;
anc[now][i]=anc[anc[now][i-1]][i-1];
maxx[now][i]=max(maxx[now][i-1],maxx[anc[now][i-1]][i-1]);
if(maxx[now][i-1]==maxx[anc[now][i-1]][i-1])
{
maxx2[now][i]=max(maxx2[now][i-1],maxx2[anc[now][i-1]][i-1]);
}
else
{
maxx2[now][i]=max(maxx2[now][i-1],maxx2[anc[now][i-1]][i-1]);
maxx2[now][i]=max(maxx2[now][i],min(maxx[now][i-1],maxx[anc[now][i-1]][i-1]));
}
}
for(ll i=head[now];i;i=ne[i])
{
ll nex=to[i];
if(nex!=pa)
{
dep[nex]=dep[now]+1;
anc[nex][0]=now;
maxx[nex][0]=val[i];
dfs(nex,now);
}
}
}
pair<ll,ll> lca(ll p,ll q)
{
ll ans1=0,ans2=0;
if(dep[p]<dep[q]) swap(p,q);
ll d=dep[p]-dep[q];
for(ll i=0;i<=24;i++)
{
if((1<<i)&d)
{
if(maxx[p][i]>ans1)
{
ans2=max(ans2,ans1);
ans1=maxx[p][i];
}
ans2=max(ans2,maxx2[p][i]);
p=anc[p][i];
}
}
if(p==q) return make_pair(ans1,ans2);
for(ll i=24;i>=0;i--)
{
if(anc[p][i]!=anc[q][i])
{
if(maxx[p][i]>ans1) ans2=max(ans2,ans1),ans1=maxx[p][i];
else if(maxx[p][i]<ans1) ans2=max(ans2,maxx[p][i]);
ans2=max(ans2,maxx2[p][i]); p=anc[p][i];
if(maxx[q][i]>ans1) ans2=max(ans2,ans1),ans1=maxx[q][i];
else if(maxx[q][i]<ans1) ans2=max(ans2,maxx[q][i]);
ans2=max(ans2,maxx2[q][i]); q=anc[q][i];
}
}
if(maxx[p][0]>ans1) ans2=max(ans2,ans1),ans1=maxx[p][0];
else if(maxx[p][0]<ans1) ans2=max(ans2,maxx[p][0]);
ans2=max(ans2,maxx2[p][0]);
if(maxx[q][0]>ans1) ans2=max(ans2,ans1),ans1=maxx[q][0];
else if(maxx[q][0]<ans1) ans2=max(ans2,maxx[q][0]);
ans2=max(ans2,maxx2[q][0]);
return make_pair(ans1,ans2);
}
int main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&u[i],&e[i],&w[i]);
temp[i]=i;
}
ll ans=kruskal();
dfs(1,1);
ll cha=987654321;
for(ll i=1;i<=m;i++)
if(!vis[i])
{
ll x=u[i],y=e[i];
pair<ll,ll> now=lca(x,y);
if(now.first==w[i])
cha=min(cha,w[i]-now.second);
else
cha=min(cha,w[i]-now.first);
}
printf("%lld\n",ans+cha);
}