严格次小生成树
程序员文章站
2022-04-11 10:24:34
...
链接:https://ac.nowcoder.com/acm/contest/1057/G
复制
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
输出
11
思路
严格次小生成树与最小生成树只有一条边不同
#include <stdio.h>
#include <algorithm>
#include <queue>
#include <math.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
int n,m,tot,t;
ll tem;
int head[100005],nex[200005],ver[200005],d[200005];
int dep[100005],maxi[100005][20],mini[100005][20],fa[100005][20],f[100005];//maxi存储最大值的边,mini存储次大值的边
struct node //存原图
{
int u,v,d;
bool vis;
bool operator<(const node &a)const{return d<a.d;};
}e[300005];
void add(int u,int v,int z) //存最小生成树
{
nex[++tot]=head[u];
ver[tot]=v;
d[tot]=z;
head[u]=tot;
}
int find(int x)
{
return x==f[x]?x:f[x]=find(f[x]);
}
void kruskal()
{
for(int i=1;i<=n;i++)f[i]=i;
sort(e+1,e+m+1);
for(int i=1;i<=m;i++)
{
int x=find(e[i].u),y=find(e[i].v);
if(x==y)continue;
f[x]=y;
e[i].vis=1;
add(e[i].u,e[i].v,e[i].d);
add(e[i].v,e[i].u,e[i].d);
tem+=e[i].d;
}
}
void bfs()
{
queue<int>q;
q.push(1);
dep[1]=1;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i;i=nex[i])
{
int v=ver[i];
if(dep[v])continue;
dep[v]=dep[u]+1;
fa[v][0]=u;
maxi[v][0]=d[i]; //记得赋值
mini[v][0]=-INF;
for(int j=1;j<=t;j++) //来更新最大值和次大值
{
fa[v][j]=fa[fa[v][j-1]][j-1];
maxi[v][j]=max(maxi[v][j-1],maxi[fa[v][j-1]][j-1]);
if(maxi[v][j-1]>maxi[fa[v][j-1]][j-1])
mini[v][j]=max(maxi[fa[v][j-1]][j-1],mini[v][j-1]);
else if(maxi[v][j-1]==maxi[fa[v][j-1]][j-1])
mini[v][j]=max(mini[v][j-1],mini[fa[v][j-1]][j-1]); //次大不等于最大(严格限制)
else //因为若最大和当前更新权一样时,我们要用次大和当前权跟新,但如果次大等于最大,则错误
mini[v][j]=max(maxi[v][j-1],mini[fa[v][j-1]][j-1]);
}
q.push(v);
}
}
}
int LCA(int x,int y) //求出公共祖先,为什么不想bfs一样在bfs中把 最大值和次大值求出来呢 因为 我们在计算时要顾及两个最大,次大 这样很麻烦
{
if(dep[x]>dep[y])swap(x,y);
for(int i=t;i>=0;i--)
if(dep[fa[y][i]]>=dep[x])y=fa[y][i];
if(x==y) return x;
for(int i=t;i>=0;i--)
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int get_max(int x,int y,int mx) //x为一个点,y为它的祖先 求出x到y 不等于当前权的最大值
{
int ans=-INF;
for(int i=t;i>=0;i--)
{
if(dep[fa[x][i]]>=dep[y])
{
if(maxi[x][i]!=mx)ans=max(ans,maxi[x][i]); //最大边如果等于当前权,则用次大值更新
else ans=max(ans,mini[x][i]);
x=fa[x][i];
}
}
return ans;
}
void solve()
{
ll ans=2147483647000000;
for(int i=1;i<=m;i++)
{
if(!e[i].vis)
{
int u=e[i].u,v=e[i].v,dis=e[i].d;
int lca=LCA(u,v);
int now1=get_max(u,lca,dis); //分别求出两条边到祖先的不等于当前权的最大值
int now2=get_max(v,lca,dis);
ans=min(ans,tem-max(now1,now2)+dis);
}
}
printf("%lld\n",ans);
}
int main()
{
scanf("%d%d",&n,&m);
t=(int)(log(n)/log(2))+1;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].d);
kruskal();
bfs();
solve();
}