kruskal + LCA
算法:先求出最小生成树,然后遍历所有未访问过的边(u、v、w),用 w 替换路径 (u ,v)中的最大值,对所有的答案取最小值。即可得到次小生成树。
- 如果想要得到严格次小生成树,那么就处理出路径(u,v) 中的最大值和次大值。
练习题
P4180 [BJWC2010]严格次小生成树
链接:https://www.luogu.com.cn/problem/P4180
题意:求严格次小生成树
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10,maxm=3e5+10,inf=2e9;
namespace LCA
{
const int N=25;
int head[maxn],cnt;
struct Edge
{
int nxt,to,w;
}edges[maxm];
void add(int u,int v,int w)
{
edges[++cnt]={head[u],v,w};
head[u]=cnt;
}
int dis1[maxn][N+5],dis2[maxn][N+5];
int fa[maxn][N+5],depth[maxn];
void init()
{
cnt=0;
memset(head,-1,sizeof(head));
}
void dfs(int u,int f)
{
fa[u][0]=f;
depth[u]=depth[f]+1;
for(int i=1;i<=N;++i)
{
fa[u][i]=fa[fa[u][i-1]][i-1];
int k[4]={dis1[u][i-1],dis1[fa[u][i-1]][i-1],
dis2[u][i-1],dis2[fa[u][i-1]][i-1]};
sort(k,k+4);
dis1[u][i]=k[3];
int p=2;
while(p>=0&&k[p]==k[p+1]) p--;
dis2[u][i]=(p==-1?-inf:k[p]);
}
for(int i=head[u];i!=-1;i=edges[i].nxt)
{
int v=edges[i].to;
if(v==f) continue;
dis1[v][0]=edges[i].w;
dis2[u][0]=-inf;
dfs(v,u);
}
}
int lca(int u,int v)
{
if(depth[u]>depth[v]) swap(u,v);
int dis=depth[v]-depth[u];
for(int j=0;dis;j++)
{
if(dis&1) v=fa[v][j];
dis>>=1;
}
if(u==v) return u;
for(int i=N;i>=0;--i)
{
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
}
return fa[u][0];
}
int query(int u,int v,int val)
{
int res=-inf;
if(depth[u]<depth[v]) swap(u,v);
int dis=depth[u]-depth[v];
for(int i=N;i>=0;--i)
{
if(dis>>i&1)
{
if(val!=dis1[u][i]) res=max(res,dis1[u][i]);
else res=max(res,dis2[u][i]);
u=fa[u][i];
}
}
return res;
}
};
int n,m;
int fa[maxn],used[maxm];
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
struct Edge
{
int u,v,w;
bool operator<(const Edge& b) const
{
return w<b.w;
}
}edges[maxm];
ll kruskal()
{
int cnt=0;
ll ans=0;
sort(edges+1,edges+1+m);
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i)
{
int u=find(edges[i].u);
int v=find(edges[i].v);
if(u==v) continue;
fa[u]=v;
used[i]=1;
LCA::add(edges[i].u,edges[i].v,edges[i].w);
LCA::add(edges[i].v,edges[i].u,edges[i].w);
ans+=edges[i].w;
cnt++;
if(cnt==n-1) break;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
LCA::init();
for(int i=1;i<=m;++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
edges[i]={u,v,w};
}
ll sum=kruskal();
LCA::dfs(1,0);
ll ans=9e18;
for(int i=1;i<=m;++i)
{
if(used[i]) continue;
int u=edges[i].u,v=edges[i].v,w=edges[i].w;
int lca=LCA::lca(u,v);
int x=LCA::query(u,lca,w);
int y=LCA::query(v,lca,w);
if(max(x,y)>-inf) ans=min(ans,sum-max(x,y)+w);
}
printf("%lld\n",ans);
return 0;
}
The Unique MST POJ - 1679
链接:http://poj.org/problem?id=1679
题意:求非严格次小生成树
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=100+10,maxm=10000+10,inf=2e9;
namespace LCA
{
const int N=25;
int head[maxn],cnt;
struct Edge
{
int nxt,to,w;
}edges[maxm];
void add(int u,int v,int w)
{
edges[++cnt].to=v;
edges[cnt].nxt=head[u];
edges[cnt].w=w;
head[u]=cnt;
}
int dis1[maxn][N+5],dis2[maxn][N+5];
int fa[maxn][N+5],depth[maxn];
void init()
{
cnt=1;
memset(head,-1,sizeof(head));
}
void dfs(int u,int f)
{
fa[u][0]=f;
depth[u]=depth[f]+1;
for(int i=1;i<=N;++i)
{
fa[u][i]=fa[fa[u][i-1]][i-1];
int k[4]={dis1[u][i-1],dis1[fa[u][i-1]][i-1],
dis2[u][i-1],dis2[fa[u][i-1]][i-1]};
sort(k,k+4);
dis1[u][i]=k[3];
int p=2;
while(p>=0&&k[p]==k[p+1]) p--;
dis2[u][i]=(p==-1?-inf:k[p]);
}
for(int i=head[u];i!=-1;i=edges[i].nxt)
{
int v=edges[i].to;
if(v==f) continue;
dis1[v][0]=edges[i].w;
dis2[u][0]=-inf;
dfs(v,u);
}
}
int lca(int u,int v)
{
if(depth[u]>depth[v]) swap(u,v);
int dis=depth[v]-depth[u];
for(int j=0;dis;j++)
{
if(dis&1) v=fa[v][j];
dis>>=1;
}
if(u==v) return u;
for(int i=N;i>=0;--i)
{
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
}
return fa[u][0];
}
int query(int u,int v,int val)
{
int res=-inf;
if(depth[u]<depth[v]) swap(u,v);
int dis=depth[u]-depth[v];
for(int i=N;i>=0;--i)
{
if(dis>>i&1)
{
res=max(res,dis1[u][i]);
u=fa[u][i];
}
}
return res;
}
};
int n,m;
int fa[maxn],used[maxm];
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
struct Edge
{
int u,v,w;
bool operator<(const Edge& b) const
{
return w<b.w;
}
}edges[maxm];
ll kruskal()
{
int cnt=0;
ll ans=0;
sort(edges+1,edges+1+m);
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i)
{
int u=find(edges[i].u);
int v=find(edges[i].v);
if(u==v) continue;
fa[u]=v;
used[i]=1;
LCA::add(edges[i].u,edges[i].v,edges[i].w);
LCA::add(edges[i].v,edges[i].u,edges[i].w);
ans+=edges[i].w;
cnt++;
if(cnt==n-1) break;
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
LCA::init();
for(int i=1;i<=m;++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
edges[i].u=u;
edges[i].v=v;
edges[i].w=w;
}
ll sum=kruskal();
LCA::dfs(1,0);
ll ans=9e18;
for(int i=1;i<=m;++i)
{
if(used[i]) continue;
int u=edges[i].u,v=edges[i].v,w=edges[i].w;
int lca=LCA::lca(u,v);
int x=LCA::query(u,lca,w);
int y=LCA::query(v,lca,w);
if(max(x,y)>-inf) ans=min(ans,sum-max(x,y)+w);
}
if(ans!=sum) printf("%lld\n",sum);
else puts("Not Unique!");
}
return 0;
}