严格次小生成树
严格次小生成树
前言
洛谷最优解rank1,u1s1快是真的快,好写是真的好写,理解也很好理解
简直酸爽,舒服了
非原创,仅作解释
正文
严格次小生成树,顾名思义,权值仅仅小于最小生成树
重点解释这个查询
inline int query(int x,int y,int w)//此时的fa记录这个点能跳到的最顶端
{
int res=-inf;
x=find(x),y=find(y);//直接跳到顶端,忽略跳过的边,因为跳过的边已经用来比较过了
while(x!=y)
{
if(dep[x]<dep[y])swap(x,y);//选取更深的点
if(val[x]<w) res=max(res,val[x]);//寻找x,y之间得最大值
fa[x]=find(father[x]); //父节点连接父节点的父节点
x=find(x);//往父节点跳
}
return res;//返回值
}
其他的相信带佬们一定能看懂
完整代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
const int maxn=1000010;
const int maxm=3000010;
#define int long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
int n,m;
int w[maxn],cnt=0,val[maxn];
int fa[maxn],sum=0,vis[maxn],dep[maxn],head[maxn];
int father[maxn];//记录节点的父节点
struct node{
int u,v,w;
}g[maxm];
struct edge{
int v,next,w;
}e[maxn];
inline int read(){
char c;int x=0,f=1;
while(c<'0'|| c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while('0'<=c && c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*=f;
}
inline void add(int u,int v,int w){
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt;
}
inline int find(int x){
if(x!=fa[x]) return fa[x]=find(fa[x]);
return x;
}
bool cmp(node a,node b){
return a.w<b.w;
}
inline void kruskal()//求最小生成树
{
cnt=0;sort(g+1,g+1+m,cmp);
for(register int i=1;i<=m;++i)
{
int u=find(g[i].u),v=find(g[i].v);
if(u!=v)
{
sum+=g[i].w;
fa[u]=v;
vis[i]=1;
if(++cnt==n-1)break;
}
}
}
inline void dfs(int u,int f)//类似于树剖预处理dfs2
{
for(register int i=head[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==f)continue;
dep[v]=dep[u]+1;//子节点深度
father[v]=u;//记录父节点
val[v]=e[i].w;//u到v节点的权值
dfs(v,u);
}
}
inline int query(int x,int y,int w)//此时的fa记录这个点能跳到的最顶端
{
int res=-inf;
x=find(x),y=find(y);//直接跳到顶端,忽略跳过的边,因为跳过的边已经用来比较过了
while(x!=y)
{
if(dep[x]<dep[y])swap(x,y);//选取更深的点
if(val[x]<w) res=max(res,val[x]);//寻找x,y之间得最大值
fa[x]=find(father[x]); //父节点连接父节点的父节点
x=find(x);//往父节点跳
}
return res;//返回值
}
signed main()
{
n=read(),m=read();//读入
for(register int i=1;i<=n;++i) fa[i]=i;//初始化并查集
for(register int i=1;i<=m;++i) g[i].u=read(),g[i].v=read(),g[i].w=read();//读入
kruskal();
for(register int i=1;i<=m;++i)
if(vis[i]){
int u=g[i].u,v=g[i].v,w=g[i].w;
add(u,v,w),add(v,u,w);//建立最小生成树
}
dep[1]=1;//以1为根,深度为1
dfs(1,0);
int ans=inf;
for(register int i=1;i<=n;++i)fa[i]=i;
for(register int i=1;i<=m;++i)
if(!vis[i]){
int w=g[i].w;
int maxx=query(g[i].u,g[i].v,w);
int num=sum-maxx+w;
if(num!=sum) ans=min(ans,num);//记录严格最小生成树的值
}
printf("%lld",ans);
return 0;
}
求最小生成树的时间复杂度\(O(mlogm)\)
预处理和查询每个边只会被枚举一次\(O(m)\)
时间复杂度\(O(mlogm)\)