- admin's blog
严格次小生成树
- @ 2024-3-16 11:09:28
严格次小生成树
严格次小生成树是在最小生成树的基础上拓展的问题,它是将最小生成树中断掉一根树的,然后接上另一个边比他大的边形成的。
所以在构建严格次小生成树的时候,我们需要先生成一个最小生成树。
严格: 严格的意思是不能取等
最小生成树的建立
首先,我们需要在给定的图中建立最小的生成树,而为了简化后续的操作,我们选择使用Kruscal算法建树。 Kruscal
加边
通过加边的方式,让原本的树中出现环,然后将环上最大的边减去。这样加的边就可以让生成树尽可能的变小,
for( re int i=1;i<=cnt;i++ )
{
if( P[edge[i].id] || edge[i].from==edge[i].to ) continue; //如果这个边之前被用过/是自环,那么我们就跳过
int p=solve( edge[i].from , edge[i].to , edge[i].w );
//记录未加边前的u、v节点之间路径的最大边权
int temp=sum1+edge[i].w-p; //让生成树的大小为 最小生成树的大小+目前加的边-环上的最大边
if( temp<ans && temp>sum1 && temp!=sum1+edge[i].w )
{ //如果目前我们的生成树的大小是小于答案的,同时是大于最小生成树的,并且保证了大小的。那么答案更新
ans=temp;
}
}
环上最大边权
那么问题就出现了,我们怎么样去找环上的最大边权呢?
问题就变成了树上路径问题,即 u-v 路径的最大路径。
方法:树链剖分
将建立的生成树保存下来,然后对他进行树链剖分,要求 u-v 的路径最大值时,我们只需要跑链和线段树就可以了。
完整Code
#include<bits/stdc++.h>
#define re register
#define N 200012
#define M 600012
#define inf 12345678900000000
#define int long long
using namespace std;
int head[N],dis[N],u,v,w,n,m;
int cnt,tot,f[N],fa[N],id[N];
int siz[N],son[N],top[N],nu;
int W[N],a[N],deep[N];
int P[M];
struct node1{
int from,to,w;
int id;
bool operator < ( const node1 other ) const
{
return w<other.w;
}
}edge[M];
struct {
int to,nex,w;
}e[M];
struct node{
int ma,ma2;
}Q[M];
void addedge(int u,int v,int w)
{
tot++;
e[tot]={ v,head[u],w };
head[u]=tot;
}
int read()
{
re char c = getchar();
int x=0,f=1;
while( c<'0' || c>'9' )
{
if( c=='-' ) f=-1;
c=getchar();
}
while( c>='0' && c<='9' )
{
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
int find(int x)
{
if( x==f[x] ) return x;
return f[x]=find(f[x]);
}
void dfs1(int x)
{
siz[x]=1;
int maxson=-1;
for(re int i=head[x];i;i=e[i].nex)
{
int y=e[i].to;
if( y==fa[x] ) continue;
W[y]=e[i].w;
fa[y]=x;
deep[y]=deep[x]+1;
dfs1(y);
siz[x]+=siz[y];
if( siz[y]>maxson )
{
son[x]=y;
maxson=siz[y];
}
}
}
void dfs2(int x,int topf)
{
top[x]=topf;
nu++;
id[x]=nu;
a[nu]=W[x];
if( !son[x] ) return;
dfs2(son[x],topf);
for(int i=head[x];i;i=e[i].nex)
{
int y=e[i].to;
if( y==fa[x] || y==son[x] ) continue;
dfs2(y,y);
}
}
#define lc x<<1
#define rc x<<1|1
int getma2(int a,int b,int c,int d)
{
int h[5]={a,b,c,d};
sort( h ,h+4 );
for(re int i=3;i>=0;i--)
{
if( h[i]!=h[3] ) return h[i];
}
}
void build(int x,int l,int r)
{
if( l==r )
{
Q[x]={ a[l],-inf };
return;
}
int mid=l+r>>1;
build( lc , l , mid );
build( rc , mid+1 ,r);
Q[x].ma=max( Q[lc].ma,Q[rc].ma );
Q[x].ma2=getma2( Q[lc].ma,Q[lc].ma2,Q[rc].ma,Q[rc].ma2 );
}
node query(int x,int l,int r,int L,int R)
{
if( L<=l and r<=R )
{
return Q[x];
}
re int mid=l+r>>1;
re node temp={ -inf , inf };
if( L<=mid )
{
temp=query( lc , l , mid , L , R );
}
if( R>mid )
{
node temp2=query( rc , mid+1 , r , L , R );
temp.ma=max( temp.ma , temp2.ma );
temp.ma2=getma2( temp.ma , temp2.ma , temp.ma2 , temp2.ma2 );
}
return temp;
}
int solve(int u,int v,int w)
{
int k=-inf;
while( top[u]!=top[v] )
{
if( deep[ top[u] ] < deep[ top[v] ] )
{
swap( u , v );
}
node temp=query( 1 , 1 , n , id[ top[u] ] , id[ u ] );
if( temp.ma==w ) k=max( k , temp.ma2 );
else k=max( k , temp.ma );
u=fa[ top[u] ];
}
if( deep[ u ] > deep[v] ) swap( u , v );
node temp=query( 1 , 1 , n , id[ u ] + 1 , id[ v ] );
if( temp.ma==w ) k=max( k , temp.ma2 );
else k=max( k , temp.ma );
return k;
}
main(){
//freopen( "P4180_7.in" , "r" , stdin );
n=read();
m=read();
for( re int i=1;i<=n;i++ ) f[i]=i;
for( re int i=1;i<=m;i++)
{
u=read(),v=read(),w=read();
edge[++cnt]={ u,v,w,i };
edge[++cnt]={ v,u,w,i };
}
sort( edge+1,edge+cnt+1 );
int sum1=0;
//cout<<endl;
for(re int i=1;i<=cnt;i++)
{
int u=edge[i].from,v=edge[i].to,w=edge[i].w;
int a=find( u ),b=find( v );
if( a==b ) continue;
sum1+=w;
P[edge[i].id]=1;
f[a]=b;
addedge( u , v , w );
addedge( v , u , w );
//cout<<edge[i].from<<" "<<edge[i].to<<" "<<edge[i].w<<endl;
}
int ans=inf;
dfs1(1);
dfs2(1,1);
build(1,1,n);
//cout<<"!!!!";
//cout<<endl;
//cout<<sum1<<endl;
// cout<<endl;
for( re int i=1;i<=cnt;i++ )
{
if( P[edge[i].id] || edge[i].from==edge[i].to ) continue;
int p=solve( edge[i].from , edge[i].to , edge[i].w );
int temp=sum1+edge[i].w-p;
if( temp<ans && temp>sum1 && temp!=sum1+edge[i].w )
{
ans=temp;
//cout<<ans<<endl;
//cout<<edge[i].from<<" "<<edge[i].to<<" "<<edge[i].w<<endl;
}
}
printf("%lld",ans);
}