AcWing 356. 次小生成树(lca)

题目

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

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

输入格式

第一行包含两个整数N和M。

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

输出格式

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

数据范围

N≤105,M≤3∗105

思路

lca次小生成树。倍增找树上路径最大边即可。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;

const int N=100010,M=300010,inf=0x3f3f3f3f;

int n,m;
struct Edge
{
	int a,b,w;
	bool used;
	bool operator< (const Edge &t) const
	{
		return w<t.w;
	}
}edge[M];
int p[N];
int h[N],e[M],w[M],ne[M],idx;
int depth[N],fa[N][17],d1[N][17],d2[N][17];
int q[N];

void add(int a,int b,int c)
{
	e[idx] = b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

int find(int x)
{
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}

ll kruskal()
{
	for(int i=1;i<=n;i++) p[i]=i;
	sort(edge,edge+m);
	ll res = 0;
	for(int i=0;i<m;i++)
	{
		int a=find(edge[i].a),b=find(edge[i].b),w=edge[i].w;
		if(a!=b)
		{
			p[a]=b;
			res+=w;
			edge[i].used=true;
		}
	}
	return res;
}

void build()
{
	memset(h,-1,sizeof h);
	for(int i=0;i<m;i++)
		if(edge[i].used)
		{
			int a=edge[i].a,b=edge[i].b,w=edge[i].w;
			add(a,b,w),add(b,a,w);
		}
}

void bfs()
{
	memset(depth,0x3f,sizeof depth);
	depth[0]=0,depth[1]=1;
	q[0]=1;
	int hh=0,tt=0;
	while(hh<=tt)
	{
		int t=q[hh++];
		for(int i=h[t];~i;i=ne[i])
		{
			int j=e[i];
			if(depth[j]>depth[t]+1)
			{
				depth[j]=depth[t]+1;
				q[++tt]=j;
				fa[j][0]=t;
				d1[j][0]=w[i],d2[j][0]=-inf;
				for(int k=1;k<=16;k++)
				{
					int anc=fa[j][k-1];
					fa[j][k]=fa[anc][k-1];
					int distance[4]={d1[j][k-1],d2[j][k-1],d1[anc][k-1],d2[anc][k-1]};
					d1[j][k]=d2[j][k]=-inf;
					for(int u=0;u<4;u++)
					{
						int d=distance[u];
						if(d>d1[j][k]) d2[j][k]=d1[j][k],d1[j][k]=d;
						else if(d!=d1[j][k] && d>d2[j][k]) d2[j][k]=d;
					}
				}
			}
		}
	}
}

int lca(int a,int b,int w)
{
	static int distance[N*2];
	int cnt=0;
	if(depth[a]<depth[b]) swap(a,b);
	for(int k=16;k>=0;k--)
	{
		if(depth[fa[a][k]]>=depth[b])
		{
			distance[cnt++]=d1[a][k];
			distance[cnt++]=d2[a][k];
			a=fa[a][k];
		}
	}
	if(a!=b)
	{
		for(int k=16;k>=0;k--)
		{
			if(fa[a][k]!=fa[b][k])
			{
				distance[cnt++]=d1[a][k];
				distance[cnt++]=d2[a][k];
				distance[cnt++]=d1[b][k];
				distance[cnt++]=d2[b][k];
				a=fa[a][k],b=fa[b][k];
			}
		}
		distance[cnt++]=d1[a][0];
		distance[cnt++]=d1[b][0];
	}
	int dist1=-inf,dist2=-inf;
	for(int i=0;i<cnt;i++)
	{
		int d=distance[i];
		if(d>dist1) dist2=dist1,dist1=d;
		else if (d!=dist1 && d>dist2) dist2=d;
	}
	if(w>dist1) return w-dist1;
	if(w>dist2) return w-dist2;
	return inf;
}

int main()
{
	//freopen("test.in","r",stdin);//设置 cin scanf 这些输入流都从 test.in中读取
    //freopen("test.out","w",stdout);//设置 cout printf 这些输出流都输出到 test.out里面去
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		int a,b,c;
		cin>>a>>b>>c;
		edge[i]={a,b,c};
	}
	ll sum=kruskal();
	build();
	bfs();
	ll res=1e18;
	for(int i=0;i<m;i++)
	{
		if(!edge[i].used)
		{
			int a=edge[i].a,b=edge[i].b,w=edge[i].w;
			res=min(res,sum+lca(a,b,w));
		}
	}
	cout<<res<<endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值