Educational Codeforces Round 3 E. Minimum spanning tree for each edge(最小生成树+带权LCA+倍增)

题目链接
在这里插入图片描述
题意:给定n个点,m条边的图,要你输出对于一条边u->v来说,如果一定要让他加入生成树里,求有它的时候的生成树的最小值。
思路:我们先构造好初始的生成树,如果一条边u->v不在生成树里,那么那么我们要让新的生成树最小的话就得删去u->v的路径上的边权最大的边maxx就可以了,如果找u->v路径上的最大边呢?可以用树上带权倍增法。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int maxn=2e5+1;
int n,m,cnt=0,deep[maxn],parent[maxn][22],father[maxn],vis[maxn];
ll num[maxn][22],maxx,ans1[maxn],mm[maxn];
struct node{
	int u,v,w,id;
}s[maxn],ss[maxn];
vector<pair<int,int>>g[maxn];
bool cmp(const node &a,const node &b)
{
	return a.w<b.w;
}
int findfather(int x)
{
	if(x==father[x]) return x;
	int i=findfather(father[x]);
	father[x]=i;
	return i;
}
void dfs(int u,int fa)
{
	deep[u]=deep[fa]+1;
	parent[u][0]=fa;
	for(auto to:g[u])
	{
		if(to.first==fa) continue;
		num[to.first][0]=to.second;
		dfs(to.first,u);
	}
}
void init()
{
	for(int k=1;k<=21;++k)
	for(int i=1;i<=n;++i)
	parent[i][k]=parent[parent[i][k-1]][k-1],num[i][k]=max(num[i][k-1],num[parent[i][k-1]][k-1]);
}
void lca(int u,int v)
{
	if(deep[u]<deep[v]) swap(u,v);
	if(u!=v)
	for(int i=21;i>=0;--i) if(deep[parent[u][i]]>=deep[v]) maxx=max(maxx,num[u][i]),u=parent[u][i];
	if(u==v) return ;
	for(int i=21;i>=0;--i)
	if(parent[u][i]!=parent[v][i]) maxx=max(maxx,max(num[u][i],num[v][i])),u=parent[u][i],v=parent[v][i];
	maxx=max(maxx,max(num[u][0],num[v][0]));
}
ll kr()
{
	for(int i=1;i<=m;++i) ss[i]=s[i];
	sort(ss+1,ss+1+m,cmp);
	ll ans=0;
	for(int i=1;i<=m;++i)
	{
		int fa=findfather(ss[i].u),fb=findfather(ss[i].v),w=ss[i].w;
		if(fa!=fb)
		{
			vis[ss[i].id]=1;
			ans+=w;
			cnt++;
			father[fa]=fb;
		g[ss[i].u].push_back({ss[i].v,ss[i].w});
		g[ss[i].v].push_back({ss[i].u,ss[i].w});
			if(cnt==n-1) break;
		}
	}
	return ans;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<=n;++i) father[i]=i;
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d%d",&s[i].u,&s[i].v,&s[i].w);
		s[i].id=i;
	}
	ll ans=kr();
	deep[1]=1;
	dfs(1,0);
	init();
	for(int i=1;i<=m;++i)
	{
		if(vis[i]) printf("%lld\n",ans);
		else {
			maxx=0;
			lca(s[i].u,s[i].v);
			printf("%lld\n",ans-maxx+s[i].w);
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值