codeforces 1108F 最小生成树并查集

本文介绍了一种使用双并查集解决特定图论问题的方法,通过对比两条边是否等价来判断是否存在多棵最小生成树,进而解决边权相同情况下的最小生成树问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

让我想起了徐州的第一题。。。。还以为比那题更难

第二天早上队友跟我聊了下这题,然后我发现这题就可以用我徐州第一题的错误的思路做(虽然那题因为数据特殊性A了。。。),我们开两个并查集,对于一段相同的边,f1表示这段相同的边之前的连接情况,然后从前向后枚举这段边,如果能连就连,只更新f2,如果不能连,去f1中看看是不是在同一集合,如果不在,说明这段边中在这条边之前有一条边跟这条边是等价的,这样就会形成多棵最小生成树,需要把一条边+1,而吧这条边+1,对之后也没有影响。

之后再把f2的更新情况同步到f1中去

#include<bits/stdc++.h>
#define maxl 200010

using namespace std;

int n,m,ans;
int f1[maxl],f2[maxl];
struct ed
{
	int u,v,val;
}e[maxl];
struct node
{
	int ind,val;
};
vector <node> tmp;

bool cmp(const ed &x,const ed&y)
{
	return x.val<y.val;
}

inline void prework()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].val);
	for(int i=1;i<=n;i++)
		f1[i]=i,f2[i]=i;
	sort(e+1,e+1+m,cmp);
}

inline int find1(int x)
{
	if(f1[x]!=x)
		f1[x]=find1(f1[x]);
	return f1[x];
}

inline int find2(int x)
{
	if(f2[x]!=x)
		f2[x]=find2(f2[x]);
	return f2[x];
}

inline void mainwork()
{
	int xx,yy,x,y,nxt;
	for(int i=1;i<=m;i++)
	{
		nxt=i;
		while(e[nxt+1].val==e[nxt].val && nxt<m)
			nxt++;
		tmp.clear();
		for(int j=i;j<=nxt;j++)
		{
			xx=find2(e[j].u);yy=find2(e[j].v);
			if(xx!=yy)
			{
				f2[yy]=xx;
				tmp.push_back(node{yy,xx});
			}
			else
			{
				x=find1(e[j].u);y=find1(e[j].v);
				if(x!=y)
					ans++;
			}
		}
		int l=tmp.size();
		for(int j=0;j<l;j++)
		{
			f1[tmp[j].ind]=tmp[j].val;
			find1(tmp[j].ind);
		}
		for(int j=i;j<=nxt;j++)
			find1(e[j].u),find1(e[j].v);
		i=nxt;
	}
}

inline void print()
{
	printf("%d",ans);
}

int main()
{
	prework();
	mainwork();
	print();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值