2021年蓝桥杯省赛 E(带权并查集)

本文介绍了使用带权并查集解决一类涉及连通性更新的问题,具体为给定点集与操作序列,操作包括连接两点和对点集进行加权操作。通过额外记录每个点到根节点的距离,在每次操作2时仅更新根节点的权值,最终每个点的权值为根节点权值加上自身到根节点的距离。算法的时间复杂度为O(mα(n)),其中m是操作数,n是点的数量,α是逆 Ackermann 函数。代码实现中,使用了路径压缩来优化查找和合并操作。
摘要由CSDN通过智能技术生成

题目
题意: 给定n个点,m组操作。输出m次操作后每个点的权值。
操作1: 连接x、y
操作2: 使x及所有与x直接或间接相连的点+y
思路: 带权并查集.
操作2明显是对在一棵树上的所有点进行操作,但是暴力更新肯定寄。
额外开一个数组记录某个点的改变量,操作2只对一棵树的根节点进行操作。最后每个点的权值即 a[find(i)] + d[i],树根的值 + i到树根的偏移量,偏移量可能是负的。
操作1就正常维护并查集的合并即可。注意新合并的px到父节点py的距离是a[px] - a[py].
时间复杂度: O(mα(n))
代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef long long ll;
typedef pair<int,int> PII;
#define fir(i,a,b) for(int i=a;i<=b;++i)
#define mem(a,x) memset(a,x,sizeof(a))
int fa[N];
int d[N]; //到根的距离 
int a[N]; //值 
int find(int x)
{
    if(x != fa[x])
    {
    	int root = fa[x];
    	fa[x] = find(fa[x]);
    	d[x] += d[root];
    }
    return fa[x];
}
void Merge(int x,int y)
{
	int px = find(x);
	int py = find(y);
	if(px != py)
	{
		fa[px] = py;
		d[px] = a[px] - a[py];
	}
}
int n,m,k,T;
void solve()
{
	cin>>n>>m;
	for(int i=1;i<=n;++i) fa[i] = i;
	while(m--)
	{
		int op; cin>>op;
		if(op == 1)
		{
			int x,y; cin>>x>>y;
		    Merge(x,y);
		}
		else
		{
			int x,t; cin>>x>>t;
			int u = find(x);
			a[u] += t;
		}
	}
	for(int i=1;i<=n;++i)
	{
		if(i > 1) cout<<" ";
		cout<<a[find(i)]+d[i]; 
	}
}
signed main(void)
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	solve();
	return 0;
}
/*
4 8
1 1 2
2 1 10
2 3 5
1 4 1
2 2 2
1 1 2
1 2 4
2 2 1
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值