次小生成树

次小生成树

给定一张N个点M条边的图,在其生成的所有树中权值大小仅次于最小生成树的树被称为次小生成树。

结合模板题进行讲解。

次小生成树

给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。

设最小生成树的边权之和为sum,严格次小生成树就是指边权之和大于sum的生成树中最小的一个。

输入格式
第一行包含两个整数N和M。

接下来M行,每行包含三个整数x,y,z,表示点x和点y之前存在一条边,边的权值为z。

输出格式
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

数据范围
N≤105,M≤3∗105
输入样例:

5 6 
1 2 1 
1 3 2 
2 4 3 
3 5 4 
3 4 3 
4 5 6 

输出样例:

输出样例:

11

可将所有边中的边分为两类,最小生成树上的边,以及附加边,即将本题转换为题目闇の連鎖

对于最小生成树,每条附加边的加入,都可以使\(u->lca->v\)路径上任意一边被删除后,整张图还是树的结构,改变的值为\(val=w~now~-w~delete~\)。由于我们所需要的是最小生成树,所以要使val尽量小,但val必须大于0,否则该次的生成树仅为最小生成树的另一种方案,减去树上的其他的边才可以使其生成最小生成树的候选项。即对于每条加入的附加边,查找路径\(u->lca->v\)上小于wnow的最大边。

由于最小生成树的最小性,路径\(u->lca->v\)的边权仅会有等于\(w~now~\)和小于\(w~now~\)两种。则最后对于val的取值:

如果路径\(u->lca->v\)的最大边权等于\(w~now\)\(val=w~now-\)次大边权。

如果路径\(u->lca->v\)的最大边权不等于\(w~now\)\(val=w~now-\)最大边权。

由此可知,对于整棵树仅需要维护每段的最大边权和次大边权。

关键代码:

对于每条附加边,可以采用倍增快速得到\(lca\)。而在得到lca时,可以同时处理出路径\(u->lca->v\)的最大边权和次大边权。

即额外开两个数组,与\(f[u][j]\)同步更新,分别保存节点u向上跳2j个点时的最大边权和次大边权。次大边权的维护与val的取值同理(因为减去的数本就是加入附加边后形成的环中的次大边权)。

void dfs()
{
	...
  f[v][0]=u;
  d1[v][0]=w;
  d2[v][0]=-INF;
  for(int j=1;(1<<j)<dep[v]&&j<=20;++j)
  {
    f[v][j]=f[f[v][j-1]][j-1];
    if(d1[v][j-1]==d1[f[v][j-1]][j-1])
    {
      d1[v][j]=d1[v][j-1];
      d2[v][j]=max(d2[v][j-1],d2[f[v][j-1]][j-1]);
    }
    else
    {
      d1[v][j]=max(d1[v][j-1],d1[f[v][j-1]][j-1]);
      d2[v][j]=min(d1[v][j-1],d1[f[v][j-1]][j-1]);
    }
  }
	...
}

在求\(lca\)的过程中,用本次跳过的路的最大边权和次大边权去更新路径上的最大边权和次大边权。其实只多了一个update操作。:D

void update_(int x)
{
	if(x>val1) val2=val1,val1=x;
	else if(x>val2&&x!=val1) val2=x;
}
void update(int u,int j)
{
	update_(d1[u][j]);
	update_(d2[u][j]);
}
void lca_(int u,int v)
{
	val1=val2=-INF;
	if(dep[u]<dep[v]) swap(u,v);
	for(int j=20;j>=0;j--)
	if(dep[v]+(1<<j)<=dep[u])
	{
		update(u,j);
		u=f[u][j];
	}
	if(u==v) return;
	for(int j=20;j>=0;j--)
	if(f[u][j]!=f[v][j])
	{
		update(u,j);
		update(v,j);
		
		u=f[u][j];
		v=f[v][j];
	}
	update(u,0);
	update(v,0);
	return;
}

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3ffffff
const int N=1e5+5,M=3e5+5;
ll n,m,ans,sum,val1,val2;
ll te,tail[N];
ll dep[N],f[N][22],d1[N][22],d2[N][11];
struct t_
{
	int u,v,w;
	bool bz;
	friend bool operator<(t_ a,t_ b)
	{
		return a.w<b.w;
	}
}t[M];
struct e_
{
	int v,w,pre;
}e[N*2];

inline void add(int u,int v,int w)
{
	e[++te]=(e_){v,w,tail[u]};
	tail[u]=te;
}

int find(int x)
{
	return f[x][0]!=x?f[x][0]=find(f[x][0]):x;
}

void Kruskal()
{
	for(int i=1;i<=m;++i)
	{
		int u=t[i].u,v=t[i].v,w=t[i].w;
		int x=find(u),y=find(v);		
		if(x!=y)
		{
			ans+=w;
			t[i].bz=1;
			f[x][0]=y;
			add(u,v,w);
			add(v,u,w);
		}
	}
}
void dfs(int u)
{
	for(int i=tail[u];i;i=e[i].pre)
	{
		int v=e[i].v,w=e[i].w;
		
		if(dep[v]) continue;
		
		dep[v]=dep[u]+1;
		
		f[v][0]=u;
		d1[v][0]=w;
		d2[v][0]=-INF;
		for(int j=1;(1<<j)<dep[v]&&j<=20;++j)
		{
			f[v][j]=f[f[v][j-1]][j-1];
			if(d1[v][j-1]==d1[f[v][j-1]][j-1])
			{
				d1[v][j]=d1[v][j-1];
				d2[v][j]=max(d2[v][j-1],d2[f[v][j-1]][j-1]);
			}
			else
			{
				d1[v][j]=max(d1[v][j-1],d1[f[v][j-1]][j-1]);
				d2[v][j]=min(d1[v][j-1],d1[f[v][j-1]][j-1]);
			}
		}
		dfs(v);
	}
}

void update_(int x)
{
	if(x>val1) val2=val1,val1=x;
	else if(x>val2&&x!=val1) val2=x;
}
void update(int u,int j)
{
	update_(d1[u][j]);
	update_(d2[u][j]);
}
void lca_(int u,int v)
{
	val1=val2=-INF;
	if(dep[u]<dep[v]) swap(u,v);
	for(int j=20;j>=0;j--)
	if(dep[v]+(1<<j)<=dep[u])
	{
		update(u,j);
		u=f[u][j];
	}
	if(u==v) return;
	for(int j=20;j>=0;j--)
	if(f[u][j]!=f[v][j])
	{
		update(u,j);
		update(v,j);
		
		u=f[u][j];
		v=f[v][j];
	}
	update(u,0);
	update(v,0);
	return;
}
int main()
{
	scanf("%lld %lld",&n,&m);
	for(int i=1;i<=n;++i) f[i][0]=i;
	for(int i=1;i<=m;++i) scanf("%lld %lld %lld",&t[i].u,&t[i].v,&t[i].w);
	
	sort(t+1,t+m+1);
	Kruskal();
	
	dep[1]=1;
	f[1][0]=0;
	dfs(1);
	
	sum=INF;
	for(int i=1;i<=m;++i)
	if(!t[i].bz)
	{
		lca_(t[i].u,t[i].v);
		sum=min(sum,t[i].w-(val1!=t[i].w?val1:val2));
	}
	
	printf("%lld",ans+sum);
}
posted @ 2020-10-23 20:06  林生。  阅读(89)  评论(0)    收藏  举报