欢乐赛E

POJ1988
原题链接https://vjudge.net/contest/350953#problem/E
在这里插入图片描述
在这里插入图片描述
有1-n个方块,题目操作为将x的方块所在位置的所有方块放到y上面。最后求方块下面有多少个方块。在这里使用并查集来做
对于样例
1 6
2 4
2 6
首先把1放到6上面,2放到4上面,之后要进行 对1 2 的合并。
也就是将2 放到 1上面
在这里插入图片描述
我们以最上面的方块作为并查集的根,我们可以计算的是下面方块到根的距离以及当前集团有多少个方块
在合并之后我们显然可以得到2集团拥有了4个方块4-2距离为1
那么我们求4下面有多少就是求
在这里插入图片描述
即4-1-1(减去跟所占的1);即为4方块下面的方块的数量。
使用vis来记录方块的数量,re来录权值。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
long long pre[30005];
long long vis[30005];
long long re[30005];
long long find(long long x)
{
	if(x==pre[x])
	{
		return x;
	}
	long long t=pre[x];
	pre[x]=find(pre[x]);//压缩路径
	re[x]+=re[t];//合并时计算距离
	return pre[x];
}
void join(long long x,long long y)
{
	long long ss1=find(x);
	long long ss2=find(y);
//	printf("%lld %lld\n",ss1,ss2);
	if(ss1!=ss2)
	{
		pre[ss2]=ss1;
		re[ss2]=vis[ss1];//将另一个集团全部放到ss2上面,那么ss2到最上方方块的距离显然就是另一个集团有多少方块。
		vis[ss1]+=vis[ss2];//将两个集团加起来,更新根的集团的数量
	}
}
int main()
{
	long long i,n;
	scanf("%lld",&n);
	for(i=0; i<=30000; i++)//初始根为自己,集团只有自己一个方块,路径为0;
	{
		pre[i]=i;
		vis[i]=1;
		re[i]=0;
	}
	while(n--)
	{
		char s;
		long long x,y,c;
		getchar();
		scanf("%c",&s);
		//printf("%c ",s);
		if(s=='M')//判断题目的操作
		{
			scanf("%lld %lld",&x,&y);
			//	printf("%lld %lld\n",x,y);
			join(x,y);//合并
		}
		else
		{
			scanf("%lld",&c);
			long long ss1=find(c);//压缩路径,更新权值。
			long long sum1=vis[ss1]-1-re[c];//计算答案。
			printf("%lld\n",sum1);
			//	printf("%lld\n",c);
		}
	}	/*
		for(i=1; i<7; i++)
		{
			find(i);
			printf("%lld ",pre[i]);
		}
		cout<<endl;
		for(i=1; i<7; i++)
		{
			find(i);
			printf("%lld ",re[i]);
		}*/
	return 0;
}

发布了130 篇原创文章 · 获赞 3 · 访问量 1601

猜你喜欢

转载自blog.csdn.net/yeyuluo/article/details/103957514