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
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
11
HINT
数据中无向图无自环; (50\%) 的数据 (N leq 2 000) ,(M leq 3 000); (80\%) 的数据 (N leq 50 000), (M leq 100 000); (100\%) 的数据 (Nleq 100 000),(Mleq 300 000) ,边权值非负且不超过 (10^9) 。
题解
首先有一个我并不会证的结论,(严格)次小生成树一定是最小生成树加一条边再删一条边得到的。
回忆非严格次小生成树的做法,先处理出最小生成树上任意两点之间的路径上的最大值,然后枚举添加每一条不在最小生成树上的边 ((u,v)) ,删掉原树上 (u) 到 (v) 路径上的最大值,然后把这条边加入就可以了。
严格最小生成树与它不同的一点是,我们找的 “原树上 (u) 到 (v) 路径上的最大值” 必须小于 我们要加的边的边权。
于是用树上倍增 (lca) 来求树上每两点间路径上的最大值和次大值就好了。
代码
求次大值是个坑,每次我都有考虑不到的地方……一定要注意注意注意!
调了好久,我好弱啊……
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100005;
typedef long long ll;
struct node{
int v,len;
node *next;
}pool[N*2],*h[N];
int cnt;
void addedge(int u,int v,int len){
node *p=&pool[++cnt],*q=&pool[++cnt];
p->v=v;p->next=h[u];h[u]=p;p->len=len;
q->v=u;q->next=h[v];h[v]=q;q->len=len;
}
int n,m;
int f[N][20],fmax[N][20],gmax[N][20],dep[N];
void dfs(int u){
int v;
for(node *p=h[u];p;p=p->next)
if(!dep[v=p->v]){
dep[v]=dep[u]+1;
f[v][0]=u; fmax[v][0]=p->len; gmax[v][0]=0;
for(int j=1;j<20;j++){
f[v][j]=f[f[v][j-1]][j-1];
fmax[v][j]=max(fmax[v][j-1],fmax[f[v][j-1]][j-1]);
if(fmax[v][j-1]==fmax[f[v][j-1]][j-1])
gmax[v][j]=max(gmax[v][j-1],gmax[f[v][j-1]][j-1]);
else {
gmax[v][j]=min(fmax[v][j-1],fmax[f[v][j-1]][j-1]);
gmax[v][j]=max(gmax[v][j],max(gmax[v][j-1],gmax[f[v][j-1]][j-1]));
}
}
dfs(v);
}
}
int m1,m2;
inline void cg(int x,int i){
if(fmax[x][i]>m1) m2=max(m1,gmax[x][i]),m1=max(m1,fmax[x][i]);
else if(fmax[x][i]<m1) m2=max(m2,fmax[x][i]);
else m2=max(m2,gmax[x][i]);
}
void getMax(int x,int y){
m1=m2=0;
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--)
if(dep[f[x][i]]>=dep[y])
cg(x,i),x=f[x][i];
if(x==y) return;
for(int i=19;i>=0;i--)
if(f[x][i]!=f[y][i]){
cg(x,i); cg(y,i);
x=f[x][i]; y=f[y][i];
}
cg(x,0); cg(y,0);
}
struct edge{
int u,v,len,vis;
bool operator < (const edge &b) const{ return len<b.len; }
}ed[N*3];
int fa[N];
inline int getfa(int x) { return x==fa[x] ? x : fa[x]=getfa(fa[x]); }
void unit(int x,int y){
x=getfa(x); y=getfa(y);
fa[x]=y;
}
int main()
{
ll ans=0;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++) scanf("%d%d%d",&ed[i].u,&ed[i].v,&ed[i].len);
sort(ed,ed+m);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=0;i<m;i++)
if(getfa(ed[i].u)!=getfa(ed[i].v)){
ed[i].vis=1;
ans+=ed[i].len;
unit(ed[i].u,ed[i].v);
addedge(ed[i].u,ed[i].v,ed[i].len);
}
dep[1]=1; dfs(1);
int Mx=2*1e9;
for(int i=0;i<m;i++)
if(!ed[i].vis){
getMax(ed[i].u,ed[i].v);
if(m1==ed[i].len) Mx=min(Mx,ed[i].len-m2);
else Mx=min(Mx,ed[i].len-m1);
}
printf("%lld
",ans+Mx);
return 0;
}