2019 UESTC ACM Training for Graph[B] (最小生成树与最大边权的问题)

28 篇文章 0 订阅

题意: 一开始可以选择一条边免费的最小生成树,并求能 达到这个最小生成树的一开始选择边的方案数

首先我们可以想到答案是最小生成树去掉其中最大边权的值。
但是讨论方案数的时候,我们会发现我们并不是只让最小生成树上的所有最大边权免费,而是只要能找到一条边,而这条边可以代替树的最大边权,然后让这条边免费就行了。
如果有这么一条边存在,那么在最小生成树上加入这条边必然会有环,并且环在树上的部分一定是最小生成树上的最大边权。
因此考虑枚举每条边,由于不在最小生成树上的边加入生成树都会出现环,因此我们只需要查询这条边两个点在最小生成树中的路径中是否包含最大权值的边。
问题变成了如何查询树上两点间最大边权是否是某个值。可以树链剖分,不过也可以去掉树上的所有最大边权再做并查集,来判断两点的连通性。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<map>
#define LL long long
using namespace std;
const LL inf=0x3f3f3f3f;
const LL maxn=1e5+5;
inline void _read(LL &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
LL n,m,vis[maxn<<1],maxx,ans,tot,fa[maxn],fa1[maxn];
struct Edge{
	LL from,to,d;
	bool operator <(const Edge& h)const{
		return d<h.d;
	}
}edges[maxn<<1];
LL getfa(LL x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
LL getfa1(LL x){return fa1[x]==x?x:fa1[x]=getfa1(fa1[x]);}
int main(){
	_read(n);_read(m);
	for(LL i=1;i<=m;i++){
		_read(edges[i].from);_read(edges[i].to);_read(edges[i].d);
	} 
	for(LL i=1;i<=n;i++)fa[i]=fa1[i]=i;
	sort(edges+1,edges+1+m);
	for(LL i=1;i<=m;i++){
		LL u=edges[i].from,v=edges[i].to;
		LL fu=getfa(u),fv=getfa(v);
		if(fu!=fv)fa[fu]=fv,ans+=edges[i].d,vis[i]=1,maxx=max(maxx,edges[i].d);
	}
	ans-=maxx;
	for(LL i=1;i<=m;i++)
		if(vis[i]&&maxx!=edges[i].d){
			LL u=edges[i].from,v=edges[i].to;
			LL fu=getfa1(u),fv=getfa1(v);
			if(fu!=fv)fa1[fu]=fv;
		}
	for(LL i=1;i<=m;i++){
		if(vis[i]&&maxx!=edges[i].d)continue;
		if(vis[i]&&maxx==edges[i].d){
			tot++;
			continue;
		}
		LL u=edges[i].from,v=edges[i].to;
		LL fu=getfa1(u),fv=getfa1(v);
		if(fu!=fv)tot++;
	}
	cout<<ans<<" "<<tot;
		
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值