次小生成树: 显然就是除开最小生成树外最小的一个生成树。
非严格次小生成树: 权值和 <= 最小生成树。
严格次小生成树: 权值和 < 最小生成树
求解: 每次在非最小生成树的边里找一条,将这条边加入树,此时一定形成环,再删去环中除该边外最大的一条边。依次枚举每条边,找到 加入边-删除边 最小的一种情况,即可求出非严格次小生成树。
当加入边的权值=删去边的权值时,就不是严格的了,所以可以再记录环中第二大的边,当非严格时,删去第二大的边,即可变为严格的。
寻找环中的最大/次大边: 先求出边的两端点的LCA(最近公共祖先),再分别求端点1到lca的最值和端点2到lca的最值,最后取最大值即可。找最值时也用倍增的思想先记录,稍微难一些。
用Kruskal的时间复杂度:
O(nlogn+mlogm),倍增是nlogn ,Kruskal堆优化是mlogm。
例题:严格次小生成树
#include
#define MAXN 100005
#define MAXM 300005
#define MAXPOW 15
#define INF 100000000000
#define ll long long
using namespace std;
struct EDGE
{
long long id,a,b,v;
long long vis;
};EDGE edge[MAXM];
struct TO
{
long long id,v;
};
struct NODE
{
long long f,deep;
vector<TO>to;
};NODE node[MAXN];
long long f[MAXN];
long long find(long long x){
if(f[x]==x)return x;
return f[x]=find(f[x]);
}
long long f2[MAXN][MAXPOW+5];
long long max1[MAXN][MAXPOW+5],max2[MAXN][MAXPOW+5];
long long n,m,x,y,z,ans=0,ans2=INF;
priority_queue<EDGE>q1;
bool operator<(const EDGE&a,const EDGE& b) {
return a.v>b.v;}
long long LCA(long long a,long long b)
{
if(node[a].deep<node[b].deep)swap(a,b);
for(long long i=MAXPOW;i>=0;i--)
if(node[f2[a][i]].deep>=node[b].deep)
a=f2[a][i];
if(b==a) return b;
for(long long i=MAXPOW;i>=0;i--)
if(f2[b][i]!=f2[a][i])
{
a=f2[a][i];b=f2[b][i];}
return f2[b][0];
}
long long change(long long x,long long lca,long long v)
{
long long maxx1=0,maxx2=0;
long long d=node[x].deep-node[lca].deep;
for(long long i=0;i<=MAXPOW;i++){
if(d<(1<<i))break;
if(d&(1<<i)){
if(max1[x][i]>maxx1){
maxx2=max(maxx1,max2[x][i]);
maxx1=max1[x][i];
}
x=f2[x][i];
}
}
if(v!=maxx1) ans2=min(ans2,v-maxx1);
else ans2=min(ans2,v-maxx2);
return 0;
}
ll dfs(ll x)
{
for(auto& to:node[x].to){
long long i=to.id,v=to.v;
if(i==node[x].f)continue;
node[i].f=x;
node[i].deep=node[x].deep+1;
f2[i][0]=x;
max1[i][0]=v;
for(long long j=1;j<=MAXPOW;j++){
if(node[i].deep<(1<<j))break;
f2[i][j]=f2[f2[i][j-1]][j-1];
max1[i][j]=max(max1[i][j-1],max1[f2[i][j-1]][j-1]);
if(max1[i][j-1]==max1[f2[i][j-1]][j-1])
max2[i][j]=max(max2[i][j-1],max2[f2[i][j-1]][j-1]);
else{
max2[i][j]=min(max1[i][j-1],max1[f2[i][j-1]][j-1]);
max2[i][j]=max(max2[i][j],max2[f2[i][j-1]][j-1]);
max2[i][j]=max(max2[i][j],max2[i][j-1]);
}
}
dfs(i);
}
return 0;
}
int main()
{
cin>>n>>m;
for(long long i=1;i<=m;i++){
cin>>x>>y>>z;
q1.push({
i,x,y,z,0});
edge[i]={
i,x,y,z,0};
}
for(long long i=1;i<=n;i++){
f[i]=i;}
//找最小生成树
long long totedge=0;
while(!q1.empty())
{
EDGE now=q1.top();q1.pop();
long long fa=find(now.a);
long long fb=find(now.b);
if(fa!=fb){
ans+=now.v;
totedge++;
edge[now.id].vis=1;
if(fa<fb)f[fa]=fb;
else f[fb]=fa;
node[now.a].to.push_back({
now.b,now.v});
node[now.b].to.push_back({
now.a,now.v});
}
if(totedge>=n-1)break;
}
//处理LCA倍增,最大值,第二大值
dfs(1);
//处理严格次小生成树
for(long long i=1;i<=m;i++)
{
if(edge[i].vis==1)continue;
long long a=edge[i].a,b=edge[i].b;
long long lca=LCA(a,b);
change(a,lca,edge[i].v);
change(b,lca,edge[i].v);
}
cout<<ans+ans2;
return 0;
}
参考:https://www.cnblogs.com/yyys-/p/11172535.html