P1196 [NOI2002] 银河英雄传说 题解

发布时间 2023-08-09 10:45:57作者: ataraxyyeah

好吧,作为一道绿题,我还是没能够自己做出来。

我做这道题时思路:利用并查集,对于 M 询问,如果不在同一集合则将两者所在集合合并,对于 C 询问

,如果不在同一集合很好解决,如果在同一集合,我们需要解决的首要问题是如何计算出两者之间的数量

所以就从这道题出发,学习一下带权并查集吧!

思路:通过上面的分析,我们发现,只要我们能够计算两者之间的距离,这道题便迎刃而解,那么该如何

维护呢? 我们可以修改 find 函数,在每一次递推中,更新当前集合的大小,同时更新顶部节点到当前

节点的距离,最后只要算两个节点的距离差就可以了。

AC代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int fa[30005],s[30001],d[30001];//fa表示每个节点的父亲,s表示当前节点到根的距离,d表示当前所在集合的大小
int find(int x)
{
	if(x==fa[x])return x;
	else
	{
	    int k=fa[x];
	    fa[x]=find(fa[x]);
	    s[x]+=s[k];
	    d[x]=d[fa[x]];
	    return fa[x];
	}
}
int main()
{
	for(int i=1;i<=30000;i++)
    {
    	fa[i]=i;
    	s[i]=0;//初始到根的距离都为0
    	d[i]=1;//初始每个集合大小都为1
	}
	int t;
	cin>>t;
	while(t--)
	{
		char op;
		cin>>op;
		if(op=='M')
		{
			int a,b;
			cin>>a>>b;
			int aa=find(a),bb=find(b);
			fa[aa]=bb;//把 i 放在 j 后面
			s[aa]+=d[bb];//更新节点到根的距离
			d[aa]+=d[bb];//更新所在集合大小
			d[bb]=d[aa];
		}
		else if(op=='C')
		{
			int a,b;
			cin>>a>>b;
			int aa=find(a),bb=find(b);
			if(aa!=bb)
			{
				cout<<-1<<endl;
			}
			else
			{
				cout<<abs(s[a]-s[b])-1<<endl;
			}
	    }
    }   
	return 0;
}