【并查集】银河英雄传说

链接

YbtOJ 银河英雄传说

题目描述

给出两种指令,一种以M开头,表示将a,b集合合并(并且是a的整个集合接到b的整个集合的末尾)一种是C开头,表示询问a,b是否在同一集合(不在输出-1),并且同一集合的要求出a,b之间的数的个数

样例输入

4
M 2 3
C 1 2
M 2 4
C 4 2

样例输出

-1
1

思路

emm,一开始想着要记录集合末端,后面发现很难处理数与数之间的距离,然后我就在冥思苦想啊,冥思苦想,然后就看题解 查资料,就学了个带权并查集(好家伙我啥都不会
那这道题就可以这么做:
t o t tot tot数组为集合的数的个数, d i s dis dis表示从当前点到集合的头的距离
因为这道题的集合合并后是一条链,那么查询操作里的求距离就可以转化为 ∣ d i s [ a ] − d i s [ b ] ∣ − 1 \left|{dis[a]}- {dis[b]} \right| - 1 dis[a]dis[b]1(因为求的是数与数之间的距离,而不是边,所以要-1)
1.合并操作就正常合并集合,然后更新一下 t o t tot tot d i s dis dis的值(tot 父亲就是父亲加上儿子,儿子继承父亲
dis就是儿子 = dis儿子 + tot父亲
2.求集合头的操作,可以在路径上不断更新当前询问到的点的tot和dis值(tot直接继承,dis 是dis儿子 += dis父亲
3.查询操作就直接判断,然后直接按照上面给的式子去求就好了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

int n;
char c;
int fa[3000005], dis[3000005], tot[3000005];

int find(int x)
{
	if(fa[x] == x) return x;
	int faa = fa[x];
	fa[x] = find(fa[x]);
	dis[x] += dis[faa];//更新距离集合头的长度
	tot[x] = tot[faa];//继承集合大小
	return fa[x];
}

void connect(int a, int b)
{
	int x = find(a), y = find(b);
	fa[x] = y;
	dis[x] = dis[y] + tot[y];//新集合离集合头的距离就是原本这个集合头的距离 + 集合的大小
	tot[y] += tot[x];//更新集合大小
}//合并集合

int main()
{
	scanf("%d", &n);
	c = getchar();
	for(int i = 1; i <= 300005; ++i)
		fa[i] = i, tot[i] = 1;
	for(int i = 1; i <= n; ++i)
	{
		int a, b;
		c = getchar();
		while(c != 'M' && c != 'C') c = getchar();
		scanf("%d%d", &a, &b);
		if(c == 'M') 
			connect(a, b);
		else {
			int x = find(a), y = find(b);
			if(x == y) 
				printf("%d\n", abs(dis[a] - dis[b]) - 1);//求距离
			else printf("-1\n");
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值