【题目】
题目描述:
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:
∑ e ∈ E M v a l u e ( e ) < ∑ e ∈ E S v a l u e ( e ) \sum_{e\in E_M}value(e)<\sum_{e\in E_S}value(e) e∈EM∑value(e)<e∈ES∑value(e)
其中 v a l u e ( e ) value(e) value(e) 表示边 e e e 的权值。
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
输入格式:
第一行包含两个整数
n
n
n 和
m
m
m ,表示无向图的点数与边数。
接下来
m
m
m 行,每行
3
3
3 个数
x
x
x
y
y
y
z
z
z 表示,点
x
x
x 和点
y
y
y 之间有一条边,边的权值为
z
z
z 。
输出格式:
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
样例数据:
输入
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
输出
11
备注:
【数据范围】
数据中无向图无自环;
50
%
50\%
50% 的数据:
n
≤
2000
,
m
≤
3000
n≤2000,m≤3000
n≤2000,m≤3000;
80
%
80\%
80% 的数据:
n
≤
50000
,
m
≤
100000
n≤50000,m≤100000
n≤50000,m≤100000;
100
%
100\%
100% 的数据:
n
≤
100000
,
m
≤
300000
n≤100000,m≤300000
n≤100000,m≤300000;
边权值非负且不超过
1
0
9
10^9
109 。
【分析】
次小生成树的模板题啦
大致讲一下算法步骤吧
次小生成树的话,和次短路的思路其实差不多
首先,肯定还是要求出最小生成树,那么现在就枚举不在最小生成树上的边,加上这条边之后,肯定就会形成一个环,将这个环上最长的边(除了新加的那条边)删掉之后,就是一个新的生成树,在新的生成树中选个最小的就行了
现在就考虑怎么实现了,就是用倍增啦
在最小生成树中,我们用 m a x 1 [ i ] [ j ] max1[i][j] max1[i][j] 表示从 i i i 往上跳 2 j 2^j 2j 步的最大值,用 m a x 2 [ i ] [ j ] max2[i][j] max2[i][j] 表示从 i i i 往上跳 2 j 2^j 2j 步的次大值(记录次大值是因为要求的是严格次小生成树,有时不能直接用最大值)
那么假设现在要新加 ( x , y ) (x,y) (x,y) 这条边,就找 x x x 到 y y y 路径上的边权最(次)大值,直接倍增找就行了(和找 l c a lca lca 差不多)
找到之后,更新,统计最小值就行了,然后就做完了
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 1000005
#define ll long long
#define inf 1ll<<60ll
using namespace std;
int n,m,t;
struct edge{int u,v,w;}e[M];
int first[N],v[M],w[M],nxt[M],vis[M];
int dep[N],father[N],fa[N][25],Max1[N][25],Max2[N][25];
bool comp(const edge &p,const edge &q) {return p.w<q.w;}
int find(int x) {return x==father[x]?x:father[x]=find(father[x]);}
void add(int x,int y,int z) {nxt[++t]=first[x];first[x]=t;v[t]=y;w[t]=z;}
ll Kruskal()
{
int i;ll ans=0;
sort(e+1,e+m+1,comp);
for(i=1;i<=m;++i)
{
int x=find(e[i].u);
int y=find(e[i].v);
if(x!=y)
{
vis[i]=1;
father[x]=y,ans+=e[i].w;
add(e[i].u,e[i].v,e[i].w);
add(e[i].v,e[i].u,e[i].w);
}
}
return ans;
}
void dfs(int x)
{
int i;
for(i=first[x];i;i=nxt[i])
{
if(v[i]==fa[x][0]) continue;
fa[v[i]][0]=x,dep[v[i]]=dep[x]+1;
Max1[v[i]][0]=w[i],Max2[v[i]][0]=0;
dfs(v[i]);
}
}
void init()
{
int i,j;
for(j=1;j<=20;++j)
{
for(i=1;i<=n;++i)
{
fa[i][j]=fa[fa[i][j-1]][j-1];
Max1[i][j]=max(Max1[i][j-1],Max1[fa[i][j-1]][j-1]);
Max2[i][j]=max(Max2[i][j-1],Max2[fa[i][j-1]][j-1]);
if(Max1[i][j-1]<Max1[fa[i][j-1]][j-1]&&Max2[i][j]<Max1[i][j-1]) Max2[i][j]=Max1[i][j-1];
if(Max1[i][j-1]>Max1[fa[i][j-1]][j-1]&&Max2[i][j]<Max1[fa[i][j-1]][j-1]) Max2[i][j]=Max1[fa[i][j-1]][j-1];
}
}
}
int find(int x,int y,int limit)
{
int i,ans=0;
if(dep[x]<dep[y]) swap(x,y);
for(i=20;~i;--i)
{
if(dep[fa[x][i]]>=dep[y])
{
ans=max(ans,Max1[x][i]!=limit?Max1[x][i]:Max2[x][i]);
x=fa[x][i];
}
}
if(x==y) return ans;
for(i=20;~i;--i)
{
if(fa[x][i]!=fa[y][i])
{
ans=max(ans,Max1[x][i]!=limit?Max1[x][i]:Max2[x][i]),x=fa[x][i];
ans=max(ans,Max1[y][i]!=limit?Max1[y][i]:Max2[y][i]),y=fa[y][i];
}
}
ans=max(ans,Max1[x][0]!=limit?Max1[x][0]:Max2[x][0]);
ans=max(ans,Max1[y][0]!=limit?Max1[y][0]:Max2[y][0]);
return ans;
}
ll solve(ll MST)
{
int i;ll ans=inf;
for(i=1;i<=m;++i)
{
if(!vis[i])
{
int temp=find(e[i].u,e[i].v,e[i].w);
if(temp!=e[i].w) ans=min(ans,MST-temp+e[i].w);
}
}
return ans;
}
int main()
{
int i;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i) father[i]=i;
for(i=1;i<=m;++i) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
ll MST=Kruskal();
dep[1]=1,dfs(1);
init();
ll ans=solve(MST);
printf("%lld",ans);
return 0;
}