《算法竞赛进阶指南》0x41 T3 银河英雄传说

题目传送门

题目描述

有一个划分为 N 列的星际战场,各列依次编号为 1,2,…,N。

有 N 艘战舰,也依次编号为 1,2,…,N,其中第 i 号战舰处于第 i 列。

有 T 条指令,每条指令格式为以下两种之一:

M i j,表示让第 i 号战舰所在列的全部战舰保持原有顺序,接在第 j 号战舰所在列的尾部。
C i j,表示询问第 i 号战舰与第 j 号战舰当前是否处于同一列中,如果在同一列中,它们之间间隔了多少艘战舰。
现在需要你编写一个程序,处理一系列的指令。

题解

这道题目的并查集题型很好看出来,涉及到了合并的问题
但查询的问题中,我们需要求出两个船之间的间隔
我们可以先将这个问题转化一下
因为所有船都将保持一列的状态,不会像树一样有枝叶
所以他们俩之间相隔的船数即为他们之间的距离-1
但他们之间的距离也不可以直接的求解出来,我们将其再次转化
他们两船相距树根的距离之差,即为他们之间的距离
因此,我们需要维护出来每个节点与根节点之间的距离
这时候就需要用上边带权的思想了,我们在路径压缩的过程中,可以维护出每个节点与根节点之间的距离,压缩时使 d [ x ] + = d [ f a [ x ] ] d[x]+=d[fa[x]] d[x]+=d[fa[x]]
在合并两列战舰时,其中一列的根节点将变为普通节点,它到另一列的根节点的距离,即为另一列的深度,所以我们还需要维护一个数组来存储每个并查集的深度,以便合并时直接使用
而在合并后,新的并查集的深度也要随之更新
这便是边带权解决此题的具体思想

code
#include
using namespace std;
const int N=500000+10;
int fa[N],n,cnt,d[N],size[N];
int get(int x)
{
	if(fa[x]==x) return x;
	int root=get(fa[x]);
	d[x]+=d[fa[x]];
	return fa[x]=root;
}
void merge(int x,int y)
{
	int fx=get(x);
	int fy=get(y);
	if(fx!=fy)
	{
		fa[fx]=fy;
		d[fx]=size[fy];
		size[fy]+=size[fx];
	}
}
int main()
{
	int T;
	char s[10];
	int y,z;
	cin>>T;
	for(int i=1;i<=30000;i++) fa[i]=i,size[i]=1;
	for(int i=1;i<=T;i++)
	{
		scanf("%s%d%d",s+1,&y,&z);
		if(s[1]=='M') merge(y,z);
		else 
		{
			if(get(y)!=get(z)) puts("-1");
			else printf("%d\n",abs(d[y]-d[z])-1);
		}
	}
	return 0;
}

你可能感兴趣的:(算法竞赛进阶指南,c++)