牛客寒假训练营 2 B(差分,并查集)

9 篇文章 0 订阅
6 篇文章 1 订阅

题意:有n个点,m条边,每次可以把m条边里面选一个放进入图中,然后每次可以使极大联通子图的权重+1,求最少权重+1的操作次数

分析

由于min(5*n,m),那么所有边一定能够放进入图中,限制条件没有用

限制条件

  • 每次只能同时操作一个集合内的点
  • 最后的所有需求都要减少为0

由于每次只能同时操作一个集合内的点,那么可以想到并查集的思想,用并查集维护

100. 增减序列(差分+贪心)_qq12323qweeqwe的博客-CSDN博客

并查集扩展域:值域,维护一个集合共同的值

由增减序列的思想可以知道,这题只可以对一个集合中的元素进行-1操作,也就是说

当出现 1,0,1 时,集合不能再继续操作,那么也是从增减序列的题目中延伸,可以想到先让集合内的所有点都变到同样高度,然后再同时让权重-1,就是最小操作次数了

由于只有+1操作,如图,应该先让中间高的先-1到集合内所有点一样高,然后再让整个集合内的点一起--

在集合内要完成这种操作,就是先让一个集合维护权重大的点,然后不断加入权重小的点,实现并查集

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5e6 + 10;
typedef long long ll;
int a[N], p[N];
struct node
{
	int l, r;
	int w;
	bool operator <(const node b)const
	{
		return w > b.w;
	}
}le[N];
int find(int x)
{
	if (x != p[x])
		p[x] = find(p[x]);
	return p[x];
}
int main()
{
	int n, m;
	cin >> n >> m;
	for (int i = 1;i <= n;i++)
		cin >> a[i], p[i] = i;
	for (int i = 1;i <= m;i++)
	{
		cin >> le[i].l >> le[i].r;
//确保能够先处理需要高的集合元素
		le[i].w = min(a[le[i].l], a[le[i].r]);
	}
	ll res = 0;
//先处理高需求
	sort(le + 1, le + 1 + m);

	for (int i = 1;i <= m;i++)
	{
		int pa = find(le[i].l), pb = find(le[i].r);
		if (pa == pb) continue;
		//并查集操作
        res += abs(a[pa] - a[pb]);
		p[pb] = pa;
        //维护并查集的值域,并查集集合内所有元素=根结点的值
		a[pa] = a[pb] = min(a[pa], a[pb]);
	}
	for (int i = 1;i <= n;i++)
		if (find(i)== i) res += a[i];
	cout << res;
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值