叠箱子_并查集

题目:

伟肖缠绵之锻炼篇

Problem Description

话说上次伟神和肖神去买东西,由于伟神身材瘦削,体力不支所以没能把肖神想要的东西都背回来,最后还是在基地同学的帮助下,才勉强背回了价值最大的物品。对于这个事情,肖神非常不满,于是,肖神为伟神制定了一套锻炼身体的方案。肖神从网上网购了 N 个石块(1<=N<=30000)。这些石块的编号从1到N不等。肖神接着会让伟神做 P 个动作(1<=P<=100000)。这些动作包含两种:堆放和计算数量。
(1)若肖神让伟神堆放石块,她就会喊“A X Y”,伟神必须把包含Y石块的一摞石块叠放在包含X石块的一摞石块上。
(2)若肖神让伟神计算数量,她就会喊“B X”,伟神就会数X石块下有几个石块。

Input

输入的第一行有一个整数P,表示肖神会让伟神做出P个动作。从第二行开始,每一行都会进行一个动作。每个石块的编号是从1到30000以内的任意一个数字,是不确定的。

Output

请输出每次计算数量操作的结果。

Sample Input

6
A 6 1
B 1
A 4 2
A 6 2
B 3
B 4 

Sample Output

1
0
2

Author

小天

题解:

由于并查集记录的通常是结点与树根的关系,所以直接记录结点其下节点数目的方式难以实现,因此可以通过记录节点其上节点数目与整棵树的结点总数目间接求解


1.为了便于解题,首先建立一个结构体,结构体中的元素需要记录结点的父节点结点所在树的结点总数,叠放在结点之上的结点数目,初始化时注意赋值

2.对每组需叠加的箱子进行并操作,并操作的时候注意更新结点上记录的数据

3.当遇到询问操作的时候,先找到这个结点的根节点,再由式子: 树的结点总数目 - 被询问结点上面的结点数目 - 1 得到答案


代码:

#include<cstdio>
#include<cstring>
#define N 30010

typedef struct MyStruct
{
	int father;//父亲结点
	int total_num;//整棵树的结点总数
	int up_num;//在当前结点之上的结点总数目
}POINT;

POINT point[N];

int find_parent(int p)
{
	if (point[p].father==p)
	{
		return p;
	}
	else//路径压缩
	{
		int parent=point[p].father;
		point[p].father=find_parent(point[p].father);
		point[p].up_num+=point[parent].up_num;//更新每个结点其上结点的数目
		return point[p].father;//层层递归,每次递归前,point[p].up_num都已经更新完毕
	}
}

void makeup(int a,int b)//合并操作
{
	a=find_parent(a);
	b=find_parent(b);
	if (a!=b)
	{
		point[b].father=a;//将a的一堆放到b这堆上面来
		point[b].up_num=point[a].total_num;//b堆顶元素的up_num就是a堆元素的总个数
		//	这里注意,只需暂时更新树根结点b上的结点数目,所有b堆的子节点的数据会在之后的find_parent的迭代过程中更新!!注意!!
		point[a].total_num+=point[b].total_num;//更新整颗树上的结点总数目,只对根节点有效,其余子节点上的此值为废值
	}

}

int main()
{
	int n,p;//石块数和操作数
	int i;
	int a,b;//合并操作的两个堆
	int k;//问k下面有多少块石块
	int ans;
	char temp;
	for ( i = 1; i <N-1; i++)//初始化
	{
		point[i].father=i;
		point[i].total_num=1;
		point[i].up_num=0;
	}
	scanf("%d",&p);
	for ( i = 1; i <=p ; i++)
	{
		getchar();
		scanf("%c",&temp);
		if (temp=='A')
		{
			scanf("%d%d",&a,&b);
			makeup(b,a);//把含b的放到含a的上面
		}
		else
		{
			scanf("%d",&k);
			ans=point[ find_parent(k) ].total_num - point[k].up_num -1;
			printf("%d\n",ans);
		}
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值