ybtoj 躲避拥挤 并查集

这篇博客探讨了一个看似图论但实际上可以使用并查集解决的问题。通过分析样例和数据范围,博主揭示了解题思路,即通过并查集处理连通性,避免在大量询问时重复计算。在讲解过程中,博主还提到了离线处理询问以优化效率的方法,并给出了相关代码实现。

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

题目

在这里插入图片描述

样例

在这里插入图片描述

数据范围

在这里插入图片描述

解题

乍一看是图,但是并查集。其实画个图就知道了


用微软白板画的,鼠标作画。
可以看到3和5之间是5705的人气值,当询问6000时,就会有
(5,3)和(3,5)这两个组合满足题目条件,像这种求连通的题呢,就可以用并查集了,不难发现,3和5一开始属于两个并查集,但满足条件之后贡献出了两个答案,那么3和5这个并查集与2这个并查集合并之后的贡献是多少呢?
是2乘1再考虑反方向走再乘2对吧,所以现在就有了6个答案(2+4)。
但是这题如果每次询问都去重做一遍,可能会爆掉,所以考虑离线做法,具体看代码

代码

#include<bits/stdc++.h>
using namespace std;
int fa [400086];//fa[i]是i的大哥
int cnt[400086];//cnt[i]是i的帮派规模
int result=0;//单次结果
int ans[400086];
struct EDGE
{
	int u;
	int v;
	int w;
}edge[100086];//从u走到v的人气值w
struct QUESTION
{
	int w;
	int num;
}que[100086];//问的人气是多少,第几次询问
int finding(int x)//找老大
{
	if(fa[x]==x)return x;//如果这个人只服自己,说明他是老大
	else return fa[x]=finding(fa[x]);//如果他的内心还有一个伟岸存在,那么去寻找他
}
void merging(int x,int y)//帮派合并
{
	int i=finding(x);
	int j=finding(y);//找老大
	
	if(i==j)return ;//大水淹了龙王庙,不用合了
	
	result+=cnt[i]*cnt[j]*2;//不难看出它们对于路线的贡献
	fa[i]=j;//i认j为老大
	cnt[j]+=cnt[i];//规模扩大
	cnt[i]=0;//被合并了
}

bool cmp1(EDGE x,EDGE y)
{
	return x.w<y.w;
}//排序一下更好找路
bool cmp2(QUESTION x,QUESTION y)
{
	return x.w<y.w;
}//排序一下更好问路
int main()
{
	int test;
	cin>>test;
	while(test--)
	{
		int n,m,q;
		cin>>n>>m>>q;
		result=0;
		for (int i=1;i<=n;i++)
		{
			fa[i]=i;
			cnt[i]=1;
		}//初始化
		
		for (int i=1;i<=m;i++)
		{
			cin>>edge[i].u>>edge[i].v>>edge[i].w;
		}
		
		for (int i=1;i<=q;i++)
		{
			cin>>que[i].w;
			que[i].num=i;
		}
		
		sort(edge+1,edge+1+m,cmp1);
		sort(que +1,que +1+q,cmp2);
		
		int j=1;//到第几条路
		for (int i=1;i<=q;i++)//询问q次
		{
			while((j<=m)&&(edge[j].w<=que[i].w))
			{//如果路还没走完并且这条路人气值不超过询问的人气值
				merging(edge[j].u,edge[j].v);
				j++;
				//合并并记录又走完了一条路
			}
			ans[que[i].num]=result;//当没有路符合条件或走完时,将答案存进去
		}
		for (int i=1;i<=q;i++)
		{
			cout<<ans[i]<<endl;
		}
	}
	return 0;
}

点个赞再走吧~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值